实时通信需求在Web应用中一直存在,从早期的Ajax轮询到如今的WebSocket,技术选型不断演进。Spring Boot作为主流的Java服务端框架,提供了对WebSocket的原生支持,让开发者无需引入额外中间件即可建立长连接通道。本文将深入整合过程,重点分析配置背后的原理与常见陷阱。

WebSocket握手过程与Spring WebSocket核心组件
WebSocket并不是一个独立的网络协议,它需要借助HTTP完成握手。客户端发起一个带有Upgrade: websocket和Sec-WebSocket-Key头的HTTP请求,服务端返回101状态码表示协议切换成功,随后的通信就转换为基于TCP的全双工消息帧。与HTTP短连接相比,WebSocket避免了每次请求都重新建立连接和传输头部信息的开销,特别适合在线聊天、实时通知、协同编辑等场景。
Spring框架从4.0开始引入了spring-websocket模块,其中最重要的是WebSocketHandler接口。该接口定义了处理连接生命周期和消息的核心方法:afterConnectionEstablished在连接建立后触发,handleMessage负责处理收到的消息,afterConnectionClosed在连接关闭时执行清理。开发者通常继承TextWebSocketHandler或BinaryWebSocketHandler,分别处理文本和二进制消息。除了Handler,Spring还提供了HandshakeInterceptor用于在握手阶段做权限校验或属性注入,以及WebSocketConfigurer来注册端点并关联拦截器。
下面是一个最小的配置类,它注册了一个路径为/ws的端点,并允许跨域访问。注意@EnableWebSocket注解会开启Spring的WebSocket支持。
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MyWebSocketHandler(), "/ws")
.addInterceptors(new HttpHandshakeInterceptor())
.setAllowedOrigins("*");
}
}
这里setAllowedOrigins("*")允许所有来源,但在生产环境应当根据域名白名单收紧。如果前端页面与后端不同域,浏览器的同源策略会阻止WebSocket连接,此时需要显式配置允许的来源。
实现一个完整的WebSocket实时通信示例
在理解了基础组件之后,我们来实现一个简单的群聊功能。服务端维护一个ConcurrentHashMap保存所有活跃的会话,收到任意客户端消息后广播给所有在线用户。这种方式适合小规模实时通信,如果用户量较大,可以结合消息队列或Redis发布订阅来扩展。
自定义Handler需要继承TextWebSocketHandler,重写三个核心方法。在afterConnectionEstablished中将session加入集合;在handleTextMessage中遍历集合发送消息;在afterConnectionClosed中移除session。需要注意的是WebSocketSession并不是线程安全的,并发发送消息时要做好同步控制,或者使用ConcurrentWebSocketSessionDecorator来包装。
@Component
public class MyWebSocketHandler extends TextWebSocketHandler {
private static final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessions.put(session.getId(), session);
System.out.println("新连接:" + session.getId());
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
for (WebSocketSession s : sessions.values()) {
if (s.isOpen()) {
s.sendMessage(new TextMessage("收到消息:" + payload));
}
}
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
sessions.remove(session.getId());
}
}
前端使用原生JavaScript的WebSocket对象连接服务端。连接成功后会触发onopen事件,收到消息触发onmessage,发送消息直接调用send方法。这里演示了一个简单的页面对话框。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocket Demo</title>
</head>
<body>
<input type="text" id="msg" />
<button onclick="sendMsg()">发送</button>
<div id="output"></div>
<script>
var ws = new WebSocket("ws://localhost:8080/ws");
ws.onopen = function() {
console.log("连接成功");
};
ws.onmessage = function(event) {
document.getElementById("output").innerHTML += "<p>" + event.data + "</p>";
};
function sendMsg() {
var msg = document.getElementById("msg").value;
ws.send(msg);
}
</script>
</body>
</html>
需要注意的是,WebSocket的URL协议前缀为ws://或wss://(对应HTTPS)。如果后端使用HTTPS,前端必须使用wss://,否则浏览器会阻止混合内容请求。
STOMP协议与生产环境注意事项
对于简单的点对点或广播场景,原生WebSocket API足够使用。但当业务需要消息路由、用户目标地址(如给某个用户发送通知)、消息确认等高级特性时,可以考虑启用STOMP(Simple Text Oriented Messaging Protocol)子协议。Spring提供了@EnableWebSocketMessageBroker注解,配合SimpMessagingTemplate可以像使用Spring MVC一样方便地发送消息。STOMP在WebSocket之上定义了帧格式,支持订阅和发布模型,适合构建复杂实时应用。
在实际部署中,WebSocket长连接会经过反向代理(如Nginx),默认的代理超时时间通常只有几十秒,远小于WebSocket的存活时间。因此需要调整Nginx配置,升级HTTP版本并设置较长的读写超时。下面是一段典型的Nginx配置示例,关键点在于proxy_set_header Upgrade $http_upgrade和Connection "upgrade",这两行确保代理正确转发协议升级请求。
location /ws {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
此外,生产环境还要考虑心跳保活机制。浏览器端可以通过定期发送Ping帧或应用层心跳包来检测连接是否仍然有效,服务端也应当配置setMaxSessionIdleTimeout来主动关闭长时间不活跃的会话,避免资源泄漏。如果服务端是多实例部署,WebSocket会话默认存储在单机内存中,无法跨节点广播消息。此时需要将消息广播下沉到Redis或消息队列,让每个实例订阅公共频道后转发给本地连接。最后,消息体大小和并发连接数也应有明确限制,防止恶意客户端拖垮服务器。
总结来说,Spring Boot整合WebSocket并不复杂,核心在于理解协议握手和Handler生命周期。根据业务复杂度选择原生API或STOMP,并在部署时关注代理配置和集群扩展,就能构建一个稳定可靠的实时通信服务。
Spring BootWebSocket实时通信修改时间:2026-10-04 23:45:49