H5实现服务器端推送,最主流且实用的方案是WebSocket,配合SSE(服务器发送事件)作为轻量级补充;对于直播类场景,则需结合HTTP/2或WebRTC数据通道。
现在很多H5应用都在追求“实时感”,比如聊天消息、订单状态、库存变化、协同编辑,传统的HTTP请求是客户端主动拉取,服务器没法主动把数据“塞”给浏览器,H5里的服务器推送技术就成了前端工程师的必修课。
先分清三种推送方案:WebSocket、SSE、HTTP/2
你打开开发者工具,看到WebSocket连接挂在Network面板里,就说明用了长连接,这三者本质区别在于连接形态、数据格式和适用场景。
| 方案 | 连接方式 | 数据格式 | 典型场景 |
|---|---|---|---|
| WebSocket | 全双工长连接 | 二进制或文本帧 | 聊天、实时协作、游戏 |
| SSE | 单向长连接(服务器到客户端) | 纯文本UTF-8 | 通知推送、股票行情、日志流 |
| HTTP/2 Server Push | 基于HTTP/2多路复用 | HTTP资源(非实时消息) | 静态资源预加载,已很少用于实时数据 |
从2020年以后,HTTP/2 Server Push已经逐步被淘汰,因为多路复用和预加载策略的复杂性超过了收益,业内专家指出,真正做业务级推送,WebSocket和SSE才是主流。
WebSocket:H5服务器推送的绝对主力
WebSocket是HTML5定义的协议,通过一次HTTP握手升级到TCP长连接,握手时请求头带上Upgrade: websocket,服务器返回101,之后双方就能互相发消息。
前端怎么写一个WebSocket客户端
核心代码很短,但坑不少,以下是一个带重连逻辑的实例:
const ws = new WebSocket('wss://yourdomain.com/push');
ws.onopen = () => {
console.log('连接建立');
// 发送心跳
ws.send(JSON.stringify({type: 'ping'}));
};
ws.onmessage = (event) => {
// 服务器推送的数据在这里
const data = JSON.parse(event.data);
if (data.type === 'order') {
updateOrderStatus(data);
}
};
ws.onclose = () => {
// 断线重连,指数退避
setTimeout(connect, Math.min(30_000, retryCount 2000));
};
后端选型与实现路径
后端语言无所谓,Node.js用ws库或socket.io,Java用Netty或Spring WebSocket,Go用gorilla/websocket,行业共识认为,Node.js在处理高并发WebSocket连接时性价比最高,因为它事件驱动模型天然适合长连接。
关键实操点:
- 心跳机制必须做:服务器每30秒ping一次,客户端回pong,否则判定连接失效。
- 代理层配置:Nginx需要设置
proxy_read_timeout为3600s以上,并开启proxy_set_header Upgrade $http_upgrade。 - 粘性问题:多实例部署时,同一个用户的连接必须路由到同一台机器,或者用Redis pub/sub广播消息。
- 鉴权要放在握手阶段:用token作为查询参数或在Sec-WebSocket-Protocol字段中传递,别在onopen后才鉴权。
高并发下的性能考量
一台4核8G的服务器,维持几万个WebSocket长连接是没问题的,但内存占用会随连接数线性增长,每个连接大约消耗20-50KB内存,如果你要支撑百万级连接,就得用分布式架构,把连接层单独抽出来,用gateway集群承载,消息通过MQ转发。
SSE:适合单向推送的轻量方案
SSE全称Server-Sent Events,它复用了HTTP协议,服务端返回Content-Type: text/event-stream后保持连接不断开,前端用EventSource接口接收消息。
SSE比WebSocket好在哪
SSE不需要额外协议,天然支持自动重连和事件ID追踪,如果你只做服务器到客户端的推送,比如用户付款成功后通知前端修改订单状态,SSE比WebSocket更省事。
SSE的标准写法
后端返回格式必须遵循规范,每行结构如下:
data: {"orderId": 123, "status": "paid"}
中间空行表示一条消息结束,前端只需要:
const es = new EventSource('/api/push');
es.onmessage = (e) => {
const order = JSON.parse(e.data);
showToast(`订单${order.orderId}已支付`);
};
如果消息需要区分类型,可以加event:字段,前端用addEventListener监听。
SSE的局限
SSE是单通道单向的,客户端不能通过同一连接向服务器发数据,如果要发消息,得另开一个XHR或fetch,SSE使用text/plain类型,无法高效传输二进制数据,所以图片、视频这类推送就别用SSE了。
实时数据推送降级策略:WebSocket断了怎么办
实际生产环境中,手机网络切换、代理超时、浏览器后台休眠都会导致长连接断开,你不能假设WebSocket永远在线,以下是我常用的降级方案。
心跳检测与断线重连
前端维护一个lastReceivedTime,每5秒检查一次,如果超过30秒没收到任何消息(包括心跳pong),就主动关闭连接并重连,重连间隔采用指数退避:第一次1秒,第二次2秒,第三次4秒,最多间隔一分钟。
页面不可见时暂停推送
用visibilitychange监听页面状态,页面不可见时,关闭SSE连接,只保留WebSocket心跳,等页面重新可见时,立即重新连接并拉取一次全量数据。
短轮询兜底
对于非关键业务,比如通知红点数,可以保留一个30秒的短轮询接口作为兜底,当WebSocket连续重连3次失败后,自动切换到轮询模式,等连接恢复后再换回长连接。
移动端H5推送的特殊处理
在手机浏览器上,H5推送受到iOS和Android的差异化限制。
iOS Safari在后台会冻结JavaScript执行,WebSocket连接会断开或无效,所以移动端H5以下拉刷新、通知列表刷新为主,长连接只服务前台页面。
Android与iOS的差异
Android Chrome允许后台运行约5分钟,iOS Safari则严格限制后台定时器,你无法通过纯H5实现“App级”的离线推送,解决办法是引导用户将页面添加到主屏幕,或者集成原生SDK做本地通知。
省电模式与数据流优化
移动端要减少无意义的数据传输,服务器在推送时,可以设置client-info头判断设备类型,对手机端只推送精简JSON,对PC端推送完整数据,后端合并高频消息,比如订单状态每50毫秒变化一次,就缓存500毫秒后合并输出一条。
如何按场景选型:从业务出发决定方案
很多开发者在WebSocket和SSE之间纠结,我的判断标准很简单:客户端是否需要双向通信。
- 聊天、游戏、多人在线白板:必须WebSocket。
- 进度通知、告警推送、站内信:SSE足够。
- 两者混用:WebSocket处理交互数据,SSE处理广播通知。
关于h5怎么实现服务器端的推送,有个常见的误区是“SSE不可以用在生产环境”,实际上SSE的兼容性已经相当好,除IE外所有主流浏览器全部支持,如果你要兼容IE9,那才需要考虑Flash甚至ActiveX这种老古董方案。
实际项目中的混合推荐
以电商后台订单管理为例:
- 订单状态变更用WebSocket,需要用户点击“接单”回执。
- 库存预警用SSE,只推送不交互。
- 页面顶部公告用HTTP轮询,5分钟一次。
这样架构清晰,每种技术用在其最合适的位置,也方便后续维护,如果你想快速上线,直接用socket.io封装好的库,它内置了心跳、降级和重连,省不少事。
服务器推送的安全与鉴权实操
推送连接最容易出现越权漏洞,别人知道你的userId就订阅了你的消息流,这绝对不行。
连接时鉴权
推荐做法是一次性token,客户端先请求/api/getPushToken,得到短时token,然后WebSocket连接时携带该token,服务器在握手协议中校验token,成功后绑定userId到连接对象上,后续推送均按userId索引。
消息加密
如果推送数据含敏感信息,比如手机号、余额,就需要在服务器加密后推送,加密算法选AES-256-GCM,密钥不直接下发,而是通过非对称加密协商,HTTPS只保证传输层,应用层数据仍然可能被设备上的恶意脚本读取,所以关键数据还是加密稳妥。
断线消息补发
服务端需要维护“最近一条消息序号”,推送时把序号带上,客户端重连后携带上次收到的序号发起增量拉取,顺序错乱会导致业务状态异常,这个逻辑必须做成幂等。
服务器推送的带宽与成本控制
大量用户同时连接时,广播消息可能把带宽打满,写代码前要算一笔账:假设每条消息500字节,1万用户同时收到就是5MB,每秒推10条就是50MB/s,这远超普通应用服务器的出口带宽。
省钱实操:
- 按房间分桶:不全局广播,只推送订阅了特定频道的人。
- 合并推送:同一用户的多条消息拼接成一条数组下发。
- 压缩:开启
permessage-deflate扩展,数据体积能压缩70%以上。 - 边缘计算:把连接放在CDN边缘节点,源站只发一次数据,边缘节点分发到用户。
常见障碍排查清单
我在调试推送时经常遇到这些问题,直接给排查路径。
- 连接总是断开:检查Nginx代理超时时间,默认60秒就会杀连接。
- 握手失败:服务端是否开启了CORS,跨域WebSocket需要正确配置Origin白名单。
- 消息延迟高:先ping一下服务器看网络RTT,再用Wireshark抓包确认数据是否从服务端发出。
- 内存持续增长:检查是否有连接没有关闭,用
netstat -an | grep ESTABLISHED统计连接数。 - 前端收不到但后端显示已发送:可能被浏览器后台节流,切到桌面测试可排除。
服务器推送的未来趋势
WebTransport正在标准化,它基于QUIC协议,支持双向流,延迟比WebSocket更低,且头部开销更小,但截至2026年,浏览器支持仍有限,生产环境用WebSocket依旧是最稳的选择,如果你想调研常被讨论的h5服务器推送方案和价格对比,最终建议还是以WebSocket为基底,SSE做辅助,这是综合性价比最高的组合。
常见问题答疑
H5做服务器推送一定要用WebSocket吗
不一定,如果业务是单向的,比如服务端定时向客户端发送通知,SSE就能胜任,代码量少、自动重连、无需额外协议,WebSocket适合需要双向交互的场景,两者可以共存于同一个项目里。
服务器推送会不会被微信内置浏览器屏蔽
微信内置浏览器支持WebSocket,但限制较多,在iOS微信中,后台超过30秒连接可能被断开;Android微信则相对宽松,建议在微信内场景使用短轮询兜底,并引导用户点击右上角菜单在系统浏览器打开。
如何测试服务器推送的稳定性
用开源的websocket-bench工具压测连接数和消息吞吐量,观察CPU、内存、网络I/O指标,同时写一个自动化脚本,模拟断网恢复后重连和消息补全流程,确保数据不丢失,你还要在弱网环境下(比如Chrome的Network面板模拟GPRS)跑一遍客户端逻辑,避免线上出现时序问题。
H5服务器推送的技术栈已经成熟,核心不是“会不会写”,而是“怎么在真实网络环境里稳定跑起来”,把心跳、重连、鉴权、降级这四件事做扎实,你就能交付一个可靠的实时系统。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/688118.html





