当前位置: 首页 > news >正文

Spring WebSocket实战:实时通信全解析

🌐 一、为什么要用 WebSocket?HTTP 不够吗?

❌ 传统 HTTP 的局限

  • 请求-响应模式:客户端发请求 → 服务器返回结果 → 连接关闭。
  • 无法实现“服务器主动推送”数据给客户端。
  • 比如:聊天室、股票行情、实时协作编辑……如果靠浏览器每隔几秒轮询一次服务器,效率极低,延迟高。

✅ WebSocket 的优势

  • 建立一次连接后,全双工、双向通信
  • 客户端和服务器都可以随时发送消息。
  • 基于 TCP,但通过标准端口(80/443),兼容现有防火墙。
  • 初始握手是 HTTP 协议,然后“升级”为 WebSocket 协议。
GET /chat HTTP/1.1
Host: localhost:8080
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

服务器回应:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

✅ 握手成功!之后双方就可以通过这个持久连接互相发消息了。


🔗 二、WebSocket vs HTTP 架构对比

特性HTTP / RESTWebSocket
通信方式请求-响应全双工、事件驱动
连接数每次请求新建或复用短连接长连接,持续通信
路由机制URL + Method (GET/POST)没有内置路由,需自定义协议
数据语义明确(JSON/XML等)自定义格式(文本/二进制)

💡 所以:WebSocket 是底层传输协议,不规定消息含义。你需要自己定义“消息长什么样”。


🧩 三、什么时候该用 WebSocket?

适合以下场景:

场景是否推荐
新闻刷新❌ 可用 Ajax 轮询或 SSE
实时聊天✅ 必须用 WebSocket
在线协作文档✅ 强依赖实时同步
股票行情推送✅ 高频+低延迟
监控告警⚠️ 若频率不高可用长轮询

📌 关键判断标准

✅ 低延迟 + 高频 + 高并发 + 服务端主动推 = 推荐 WebSocket
❌ 网络环境复杂(如公共互联网上的代理)可能阻断 WebSocket(不支持 Upgrade 头)


⚙️ 四、Spring 如何支持 WebSocket?

Spring 提供了一整套 API 来开发 WebSocket 应用。

4.1 WebSocketHandler:处理 WebSocket 消息

最基础的接口,你可以继承它来处理连接、接收消息、关闭等事件。

public class MyHandler extends TextWebSocketHandler {@Overridepublic void handleTextMessage(WebSocketSession session, TextMessage message) {String payload = message.getPayload();session.sendMessage(new TextMessage("Echo: " + payload));}
}

注册到某个路径 /myHandler

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(new MyHandler(), "/myHandler");}
}

4.2 握手拦截器(HandshakeInterceptor)

可以在建立连接前做一些事情,比如:

  • 获取用户身份(从 HTTP Session)
  • 拒绝非法来源的连接
.addInterceptors(new HttpSessionHandshakeInterceptor())

还可以自定义 HandshakeHandler 控制整个握手过程。


4.3 部署问题(Deployment)

⚠️ 注意:Java 的 WebSocket 标准(JSR-356)有一个大坑:

它不能和 Spring MVC 的 DispatcherServlet 共享同一个入口!

这意味着:如果你只用原生 JSR-356,就不能让所有请求(包括普通 HTTP 和 WebSocket)都走同一个 DispatcherServlet。

Spring 的解决方案

使用 RequestUpgradeStrategy,针对不同容器(Tomcat/Jetty/WebLogic 等)提供适配层,使得 WebSocket 升级请求也能被 DispatcherServlet 正常处理。

所以你可以放心地在一个 Spring MVC 应用中同时处理普通请求和 WebSocket。


4.4 服务器配置(Server Configuration)

可以设置缓冲区大小、超时时间等:

@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();container.setMaxTextMessageBufferSize(8192);container.setIdleTimeout(600000); // 10分钟空闲断开return container;
}

4.5 跨域控制(Allowed Origins)

默认只允许同源访问。可配置白名单:

registry.addHandler(myHandler(), "/myHandler").setAllowedOrigins("https://yourdomain.com");

三种模式:

  1. 同源(默认)
  2. 白名单(指定域名)
  3. 允许所有("*"

⚠️ 注意:IE6~IE9 支持有限,尤其是 iframe 方式需要放开 X-Frame-Options


🔄 五、SockJS:WebSocket 的降级方案(Fallback)

为什么需要 SockJS?

因为很多老旧网络环境(公司代理、防火墙)会:

  • 屏蔽 Upgrade
  • 断开长时间空闲连接

导致 WebSocket 失败。

SockJS 的作用

✅ 尝试使用 WebSocket
❌ 失败则自动切换为 HTTP 模拟长连接(Streaming 或 Polling)

对外暴露相同的编程接口(W3C WebSocket API),开发者无需修改代码。

SockJS 工作流程

  1. 客户端请求 /info 获取服务器信息
  2. 根据浏览器能力选择传输方式:
    • ✅ WebSocket(最佳)
    • ✅ XHR Streaming(流式)
    • ✅ Long Polling(长轮询)
  3. 所有消息走统一格式:
    • 开始:o
    • 消息:a["hello"](JSON 数组)
    • 心跳:h
    • 关闭:c

URL 示例:

https://host/myapp/endpoint/{server-id}/{session-id}/websocket
https://host/myapp/endpoint/1/abc/xhr-streaming

如何启用 SockJS?

Java 配置:

registry.addHandler(myHandler(), "/myHandler").withSockJS();

XML 配置:

<websocket:mapping path="/myHandler" handler="myHandler"/>
<websocket:sockjs/>

前端引入 sockjs-client:

<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
<script>var sock = new SockJS('/myHandler');sock.onmessage = function(e) { console.log(e.data); };
</script>

❤️ SockJS 心跳机制

防止代理认为连接“闲置”而断开。

  • 默认每 25 秒发一个心跳包(h
  • 可配置 heartbeatTime
  • 使用任务调度器(TaskScheduler),建议根据 CPU 核心数调整线程池

🚪 客户端断开检测

HTTP 流或长轮询下,Servlet API 不直接通知“客户端已断开”。

Spring 的做法:

定期发送心跳 → 如果写入失败 → 认为客户端已断开

日志中会有提示,避免堆栈刷屏。


🌍 CORS 支持

SockJS 在跨域时自动添加 CORS 头:

Access-Control-Allow-Origin: http://your-origin.com
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 31536000

也可以手动禁用:

.withSockJS().setSuppressCors(true)

🖥️ SockJsClient(Java 客户端)

Spring 还提供了 Java 版本的 SockJS 客户端,用于:

  • 服务间通信(非浏览器)
  • 压力测试(模拟大量用户)

示例:

List<Transport> transports = Arrays.asList(new WebSocketTransport(new StandardWebSocketClient()),new RestTemplateXhrTransport()
);SockJsClient client = new SockJsClient(transports);
client.doHandshake(new MyHandler(), "http://example.com/sockjs");

📦 六、STOMP:基于 WebSocket 的高级消息协议

为什么需要 STOMP?

WebSocket 只是传输层,没有定义:

  • 消息类型(订阅?发布?)
  • 消息目的地(发给谁?)
  • 消息体结构

👉 所以我们需要一个应用层协议 —— STOMP(Simple Text Oriented Messaging Protocol)

STOMP 的核心概念

概念说明
SEND客户端发送消息
SUBSCRIBE客户端订阅某个主题
MESSAGE服务器向客户端推送消息
/app/hello客户端发来的目标地址(经由注解映射)
/topic/greetings广播主题
/user/me/private用户私信(点对点)

示例:Spring + STOMP 架构

1. 后端配置 STOMP 端点
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {registry.addEndpoint("/ws")     // 暴露 STOMP 端点.withSockJS();         // 支持降级}@Overridepublic void configureMessageBroker(MessageBrokerRegistry registry) {registry.setApplicationDestinationPrefixes("/app"); // 前缀:客户端发给服务的消息registry.enableSimpleBroker("/topic", "/queue");    // 内存消息代理,支持广播和队列}
}
2. 控制器接收消息
@Controller
public class GreetingController {@MessageMapping("/hello") // 对应 SEND 到 /app/hello@SendTo("/topic/greetings") // 返回值广播到该主题public GreetingMessage greeting(HelloMessage message) {return new GreetingMessage("Hello, " + message.getName());}
}
3. 前端使用 stomp.js
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@stomp/stompjs@6/umd/stomp.min.js"></script><script>const socket = new SockJS('/ws');const stompClient = Stomp.over(socket);stompClient.connect({}, function(frame) {stompClient.subscribe('/topic/greetings', function(message) {console.log(JSON.parse(message.body));});stompClient.send("/app/hello", {}, JSON.stringify({'name': 'Alice'}));});
</script>

✅ 总结:一张图看懂整体架构

[Browser] ↓ (HTTP Upgrade)
[Spring DispatcherServlet]↓
[WebSocket Handshake] → 成功?→ [WebSocket Transport]↓ 否[SockJS Fallback]↓[XHR Streaming / Long Polling]↑↓ 使用 STOMP 协议通信[STOMP Broker Relay 或 SimpleBroker]↑[@MessageMapping 方法处理]

🧠 关键要点总结

技术作用
WebSocket提供全双工通信通道
SockJSWebSocket 的兼容性兜底方案(降级)
STOMP在 WebSocket 上构建的消息协议(类似 AMQP 的轻量版)
SimpleBroker内置内存消息代理,适合小规模应用
External Broker(如 RabbitMQ)生产环境推荐,支持集群、持久化

🛠️ 实际开发建议

  1. 开发阶段:用 @EnableWebSocketMessageBroker + SimpleBroker
  2. 生产环境:集成 RabbitMQ 或 ActiveMQ 作为 STOMP Broker
  3. 跨域:合理设置 setAllowedOrigins()X-Frame-Options
  4. 安全:结合 Spring Security 控制连接权限
  5. 性能测试:用 SockJsClient 模拟千级并发连接

如果你想进一步了解某一部分(比如“如何整合 RabbitMQ 做 STOMP 中继”或“Spring Security 如何保护 WebSocket”),欢迎继续提问!

http://www.dtcms.com/a/549866.html

相关文章:

  • 网站建设及推广方案ppt模板互联网时代的营销
  • 身份证三要素实名核验API:基于OCR与可信身份认证的技术解析
  • OCR-图片内容识别
  • wordpress 网址导航页面济南seo网站关键词排名
  • VM虚拟机共享宿主机代理(Ubuntu24.04)
  • 高端网站建设收费为何比较贵网站名称写什么
  • C++容器string
  • [人工智能-大模型-110]:用通俗易懂的语言,阐述梯度下降的本质和数学原理:就是“摸着坡度下山”——哪边最陡,就往反方向走一步,反复走,直到走到谷底。
  • 龙华做棋牌网站建设哪家好哪个平台可以免费推广
  • 网站负责人可以备案怎么看网站域名
  • AIoT项目芯片选型指南:在性能、功耗与成本的十字路口
  • Vue 3 插槽(Slot)详解
  • 刘涛做的网站网络营销的优势包括
  • Spring Boot数据访问篇:整合MyBatis操作数据库
  • 丰都网站建设报价现代示范校建设专题网站
  • Flink Keyed State 详解之七
  • 中国建设银行贵州分行网站宁波建设银行管方网站
  • NVIDIA驱动更新“翻车”?解决RTX 2060在Bilibili客户端无法加载4K视频的终极指南*
  • 贵德县建设局网站校园兼职网站开发用例图
  • JavaSE知识分享——类和对象(下)
  • 企业级K8s部署:Helm+Kustomize混合策略实现零配置漂移与10分钟多环境发布
  • 上海人才中心昆明网站词排名优化
  • jQuery Growl - 实用且易于集成的通知插件
  • Manage Provisioning Access 功能详解
  • 龙岗在线网站建设网络销售网站外包
  • NVIDIA NCCL 源码学习(十六)- nccl的ibgda(GIN)
  • 深度优先搜索(DFS)
  • 协会网站建设方案wordpress 以前文章灯箱
  • PCIe学习笔记
  • 处理 rstudio-server 卡死问题