WebSocket / SockJS / STOMP 是干什么的?
1. 先用一句话理解三者关系
- WebSocket :一种"底层通信通道协议",让浏览器和服务器可以 建立一条长连接并双向实时通信。
- SockJS :一个"兼容层/降级方案",在 不支持 WebSocket 或网络环境不稳定 时,帮你用其它方式(长轮询等)模拟"类似实时连接"的效果。
- STOMP:一个"上层消息协议",在 WebSocket(或 SockJS 连接)之上,规定消息怎么组织、怎么发给谁、怎么订阅频道,类似"消息队列的用法"。
一句话总结:
WebSocket = 通道 ,SockJS = 通道的备胎/兼容 ,STOMP = 通道上的消息规范。
2. 为什么需要 WebSocket?
2.1 HTTP 的问题:不适合"服务器主动推送"
传统 HTTP 请求是这样的:
- 浏览器:我来问你一次(Request)
- 服务器:我回答你一次(Response)
如果你想要"消息实时到达"(例如聊天室新消息、股票行情、游戏状态、订单状态),服务器没法直接"主动推",只能让浏览器不停轮询:
- 每 1 秒请求一次:
/api/messages - 没有新消息也要请求,浪费资源、延迟高
2.2 WebSocket 的核心能力:长连接 + 双向
WebSocket 建立连接后:
- 连接不断开(除非主动关闭/网络断开)
- 浏览器可以随时发消息给服务器
- 服务器也可以随时推消息给浏览器(实时)
通俗例子:打电话 vs 发短信
- HTTP:像发短信。你问一句、对方回一句;想实时只能不停发短信问"你在吗?"
- WebSocket:像打电话。一旦接通,你俩可以随时说话,不用重新拨号。
3. SockJS 是干什么的?
3.1 为什么要 SockJS:现实世界不完美
理想情况是大家都能直接用 WebSocket,但现实里可能遇到:
- 某些老旧浏览器/环境不支持(现在少见,但企业内网可能还会遇到)
- 代理、防火墙、网关对 WebSocket 支持不好
- 一些网络环境会断开长连接或限制升级协议
3.2 SockJS 的做法:优先 WebSocket,不行就"降级"
SockJS 通常会做类似策略(简化描述):
- 能用 WebSocket:直接用
- 不能用:退化成 HTTP 长轮询(long-polling)等方式模拟"实时"
对业务代码来说,你仍然像在用"一个持续连接"。
通俗例子:高速公路 vs 国道
- WebSocket:高速公路,快、实时、双向
- SockJS:导航软件。优先走高速;高速封了就走国道(慢点,但保证能到)
4. STOMP 是干什么的?
4.1 WebSocket 只是"通道",缺少"消息语义"
WebSocket 只负责把一段文本/二进制从 A 发到 B。
但在真实业务里你经常需要:
- 订阅某个频道:
/topic/chat/room1 - 给特定用户发消息:
/user/123/queue/notify - 广播、点对点、确认、错误处理等
- 统一消息格式、路由规则
如果不用上层协议,你得自己约定一套"消息规范",比如 JSON 里写:
json
{ "type": "subscribe", "topic": "room1" }
然后后端还得自己解析、路由、维护订阅关系。
4.2 STOMP:一个"消息协议",像 WebSocket 上的 HTTP
STOMP(Simple Text Oriented Messaging Protocol)提供:
- SEND:发送消息到某目的地(destination)
- SUBSCRIBE:订阅某目的地
- UNSUBSCRIBE:取消订阅
- ACK/NACK:确认机制(一些场景用)
- 统一的帧格式(frame),便于实现消息路由
通俗例子:快递公司
- WebSocket:运输车(能把包裹运过去)
- STOMP:快递单规则(写清楚"发件人、收件人、目的地、是否签收")
- 没快递单你也能运货,但分拣会很麻烦;有规则后就能自动化分发
5. 三者通常怎么组合使用?(典型场景)
5.1 你可能见过:SockJS + STOMP(尤其在 Spring 生态)
很多项目里会出现组合:
- 前端:通过 SockJS 建立连接(确保兼容)
- 在连接之上:使用 STOMP 来订阅/发送消息
- 后端:用消息路由(broker)把消息转发到不同 topic/user
业务层面你会用到的概念通常是:
- topic:广播频道(聊天室房间、公共通知)
- queue:队列/点对点(某用户的私信、个人通知)
- destination:统称消息目的地(topic/queue 都是一种 destination)
5.2 聊天室例子(非常典型)
需求:
- 所有人订阅
room1,有人发言后所有人都收到
做法:
- 客户端
SUBSCRIBE /topic/room1 - 客户端发送消息
SEND /app/chat/room1 - 服务端收到后
SEND /topic/room1(或由框架自动广播)
6. 什么时候选哪个?
6.1 只用 WebSocket(最常见、最直接)
适合:
- 你能控制客户端环境(现代浏览器/APP 内置 WebView)
- 不需要复杂订阅路由,消息格式你自己约定
- 追求轻量、低依赖
6.2 WebSocket + STOMP(更工程化)
适合:
- 你有"频道订阅/广播/点对点"模型
- 希望协议标准化、减少自定义协议成本
- 后端用 Spring WebSocket、消息代理等生态
6.3 SockJS + STOMP(兼容性/企业网络更稳)
适合:
- 需要适配更复杂的网络环境、老旧系统
- 希望"能连上"优先于"极致性能"
- 常见于企业 IM、OA 通知、内网系统
7. 常见误区澄清
7.1 "用了 WebSocket 就一定比 HTTP 快?"
不一定。
WebSocket 的优势是 实时推送、减少重复握手。但如果你只是偶尔请求一次数据,HTTP 可能更简单更合适。
7.2 "SockJS 是 WebSocket 的替代品吗?"
更准确说是 WebSocket 的兼容与降级方案。能用 WebSocket 时它也会优先用 WebSocket。
7.3 "STOMP 等于 WebSocket 吗?"
不是。
- WebSocket:传输层通道
- STOMP:消息层协议(跑在通道上)
8. 你可以怎么把它们记住(最简心智模型)
- WebSocket:我跟服务器拉了一根"网线"(长连接)
- SockJS:网线拉不了时,我用"对讲机/短信频繁确认"来凑合
- STOMP:我规定"说话格式"和"频道/收件箱",这样系统能自动分发