WebSocket 是一种全双工通信协议,相比 HTTP 的"请求-响应"模式,服务端可以主动向客户端推送消息。本文记录我在学习 Spring Boot 集成 WebSocket 过程中的笔记。
为什么需要 WebSocket?
HTTP 协议是单向的,客户端请求 → 服务端响应,服务端无法主动推送。这在以下场景中有问题:
- 即时通讯(聊天室)
- 实时通知/推送
- 实时数据大屏
- 在线协作编辑
在 WebSocket 之前,常见的"伪推送"方案:
| 方案 | 缺点 |
|---|---|
| 短轮询 | 大量无效请求,浪费资源 |
| 长轮询 | 连接长时间占用,并发能力差 |
| SSE | 仅支持服务端→客户端单向推送 |
WebSocket 的优势:全双工、低延迟、节省带宽、支持文本和二进制。
WebSocket 协议握手过程
客户端 服务端
| |
|--- HTTP Upgrade 请求 ----------------->|
| Upgrade: websocket |
| Sec-WebSocket-Key: xxx |
| |
|<-- HTTP 101 Switching Protocols ------|
| |
|<====== 双向 WebSocket 数据帧 ========>|
握手基于 HTTP,端口复用 80/443。握手后协议切换为 ws:// 或 wss://。
Spring Boot 集成方式
有三种方式:
| 方式 | 适用场景 | 推荐度 |
|---|---|---|
| 原生 WebSocket API | 简单场景 | 低 |
| Spring WebSocketHandler | 需 Spring 集成但不需 STOMP | 中 |
| STOMP over WebSocket | 企业级应用 | 推荐 |
推荐 STOMP 的原因:声明式路由(@MessageMapping)、内置心跳、支持点对点和广播、前端有成熟库。
STOMP 协议简介
STOMP 跑在 WebSocket 之上,提供"目标地址"和"订阅"概念:
SUBSCRIBE
destination:/topic/chat # 订阅频道
SEND
destination:/app/chat # 发送消息
核心概念:三种目标前缀
| 前缀 | 含义 |
|---|---|
/app | 应用层入口,消息路由到 @MessageMapping |
/topic | 广播频道,所有订阅者都能收到 |
/user | 用户私有频道,Spring 自动路由到特定用户 |
工程落地
Maven 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
STOMP 配置类
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic", "/queue"); // 消息代理
registry.setApplicationDestinationPrefixes("/app"); // 应用层前缀
registry.setUserDestinationPrefix("/user"); // 用户前缀
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
.withSockJS(); // SockJS 降级
}
}
消息处理器
@Controller
public class ChatController {
@Autowired
private SimpMessagingTemplate messagingTemplate;
// 广播消息
@MessageMapping("/chat")
@SendTo("/topic/chat")
public ChatMessage handleChat(ChatMessage message) {
return message;
}
// 点对点消息
@MessageMapping("/private")
public void handlePrivate(ChatMessage message, Principal principal) {
messagingTemplate.convertAndSendToUser(
message.getTo(), "/queue/private", message
);
}
}
高级话题
心跳机制
registry.enableSimpleBroker("/topic", "/queue")
.setHeartbeatValue(new long[]{10000, 10000}); // 10秒心跳
集群方案
SimpleBroker 是内存级的,不支持集群。生产环境用 RabbitMQ STOMP:
registry.enableStompBrokerRelay("/topic", "/queue")
.setRelayHost("rabbitmq-host")
.setRelayPort(61613);
总结
Spring Boot WebSocket 的核心是 STOMP 协议带来的"消息队列"编程模型:
/app:应用入口/topic:广播/user:点对点
理解这三个前缀的分工,就掌握了 Spring WebSocket 的核心。