JavaWeb HTTP长连接完整梳理
先厘清概念: HTTP 本身是短连接、无状态协议。 HTTP长连接分两类,很多人容易混淆:
- HTTP/1.1 Keep-Alive(连接复用,底层TCP不马上断开)
- 业务层面长连接推送方案:轮询 / SSE / WebSocket(基于HTTP握手升级)
日常开发口中说的「http长连接」有两层含义,分开讲场景、解决问题、注意事项。
一、第一层:HTTP 1.1 Keep-Alive(TCP连接复用)
原理
一次TCP握手建立连接,完成一次HTTP请求响应后,TCP不立即关闭 ,短时间内可以复用这条连接发送多次HTTP请求。 默认HTTP/1.1 开启Keep-Alive。
✅ 解决什么问题
- 避免频繁「TCP三次握手、四次挥手」开销,减少延迟、降低CPU开销;
- 同一客户端连续请求多个接口(页面、静态资源)提升性能。
📌 JavaWeb里典型场景
- 浏览器访问页面,并行加载js/css/image;
- 移动端APP短时间连续调用多个后端接口;
- Nginx + Tomcat 反向代理内网通信。
⚠️ 需要注意的坑
- 不是永久保持 服务端有超时时间(Tomcat
connectionTimeout,Nginxkeepalive_timeout),空闲超时自动断开。 - 连接池必须匹配 Tomcat Connector最大连接数、Nginx upstream keepalive数量要协调,避免连接耗尽。
- 不要误以为它能实现"服务端主动推送消息" Keep-Alive只是复用连接收发请求 ,依然只能客户端主动发起请求,服务端不能主动发消息。
重点区分:Keep-Alive ≠ 消息推送长连接。
二、第二层:业务实时推送场景(大家业务开发最关心)
如果需要 服务端主动向客户端推送数据,有三类基于HTTP体系的方案:
- 短轮询(普通HTTP反复请求)
- 长轮询 Long Polling(广义HTTP长连接)
- SSE(Server-Sent Events,单向HTTP长连接)
- WebSocket(HTTP握手后升级协议,不再是纯HTTP)
1)长轮询 Long Polling(标准HTTP长连接方案)
流程
客户端发起HTTP请求 → 服务端hold住请求不立刻返回; 有消息/超时到达再响应;客户端收到后立刻再次发起新请求。
✅ JavaWeb适用场景
- 小程序/普通网页 不能使用WebSocket 的环境;
- 企业后台消息通知、工单变更、审批提醒;
- 老旧网络环境,防火墙严格拦截WebSocket(很多防火墙屏蔽101 Switching Protocol)。
解决问题
对比普通短轮询: 短轮询:高频请求,大量无效空请求,浪费带宽、压服务器; 长轮询:没有消息时不频繁返回,大幅降低无效请求。
⚠️ 注意事项
- Tomcat同步IO模式下,一个长轮询占用一个工作线程 ! 大量在线用户直接打满线程池,服务卡死。 👉 解决方案:使用 异步Servlet AsyncContext(Spring MVC DeferredResult),释放容器线程。
- 需要设置合理超时(30s~90s),防止连接无限挂住;
- 客户端断线要能感知,释放服务端资源;
- 集群环境:消息要借助MQ(Redis PubSub/RabbitMQ)广播,不能内存缓存。
2)SSE Server-Sent Events(纯HTTP单向长连接)
基于标准GET请求,持续流式文本输出 text/event-stream。
场景
监控大屏实时数据、日志实时输出、实时公告、大盘指标刷新。
优点
原生HTTP,无需协议升级;浏览器原生支持;实现简单。
局限
只能服务端→客户端单向推送,客户端不能实时上行发送消息。
3)WebSocket(HTTP握手升级)
握手阶段使用HTTP,随后切换为websocket协议,严格来说不属于HTTP长连接,但经常一起讨论。 双向通信,聊天、实时协同、扫码登录、在线状态首选。
三、汇总:JavaWeb 各类"HTTP长连接"适用场景对照表
1. Keep-Alive(连接复用)
目标:优化多次请求网络性能 场景:网页资源加载、APP连续调用接口 能力:只能客户端主动请求,不能推送
2. Long Polling 长轮询
目标:弱实时消息推送,兼容恶劣网络、无法开启WS的环境 场景:后台审批通知、小程序消息、扫码等待确认 能力:伪双向,靠持续挂起请求实现推送
3. SSE
目标:单向实时数据流 场景:监控面板、实时日志、行情展示 能力:仅服务端推送给客户端
4. WebSocket(HTTP握手升级)
目标:低延迟双向实时通信 场景:聊天、协同编辑、设备实时状态、物联网上报
四、开发高频踩坑清单(重点,直接落地参考)
1. 线程风险(头号大坑)
❌ 同步Servlet写长轮询:每一条连接占用Tomcat线程,上千用户直接线程打满。 ✅ 使用 SpringMVC DeferredResult / AsyncServlet 异步模式。
2. 集群部署问题
单机内存保存连接 → 集群下消息无法跨节点推送。 方案:Redis发布订阅、消息队列做跨实例消息广播。
3. 超时、断连重连
网络波动、防火墙、代理会主动掐断空闲连接; 客户端必须实现自动重连逻辑,不能一次断连就永久失效。
4. 中间件截断问题
Nginx、网关默认有 proxy_read_timeout,长时间无数据会主动断开长连接。 必须调大超时,或者服务端定时发送心跳包(ping)维持链路。
5. 浏览器并发限制
浏览器对同一域名并行HTTP连接数量有限制;大量SSE/长轮询会占用连接配额,阻塞其他接口。
6. 和普通业务隔离
长连接服务尽量单独部署/单独线程池,避免实时推送业务耗尽资源,影响普通CRUD接口。
7. 内存泄漏风险
客户端下线没有正常关闭,服务端持有异步上下文不释放,堆积内存。 需要设置超时兜底、清理机制。
五、一句话选型建议(贴合业务开发)
- 只是页面加载、APP多次请求 → 依靠容器默认 Keep-Alive 即可;
- 需要消息推送、环境不能上WebSocket → 优先长轮询(DeferredResult);
- 只需要单向展示实时大屏数据 → SSE;
- 需要双向频繁交互(聊天、设备控制)→ WebSocket。