2008年诞生的WebSocket协议,彻底改变了传统Web应用的通信模式。与HTTP这种"一问一答"的短连接不同,WebSocket建立了持久化的全双工通道。想象一下打电话和发短信的区别——前者可以随时自由对话,后者需要反复建立连接,这就是WebSocket带来的根本性变革。
在Spring Boot生态中集成WebSocket,开发者可以获得三大核心优势:
我去年为某物流系统改造实时位置追踪功能时,用WebSocket替代原来的HTTP长轮询后,服务器负载直接下降了65%,客户端的电量消耗也显著降低。这种性能提升在需要高频更新的场景(如在线协作、金融行情、IoT监控)中尤为明显。
首先在pom.xml中添加关键依赖:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>sockjs-client</artifactId>
<version>1.5.1</version>
</dependency>
配置类需要继承WebSocketMessageBrokerConfigurer:
java复制@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
.withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}
}
关键提示:生产环境务必配置跨域白名单,避免直接用
*通配符
STOMP(Simple Text Oriented Messaging Protocol)为WebSocket提供了消息语义。就像HTTP定义了请求方法(GET/POST等),STOMP定义了:
在Spring中典型的消息流:
code复制客户端 --SEND--> @MessageMapping方法 --> 处理逻辑 --> @SendTo指定广播
java复制@Controller
public class ChatController {
@MessageMapping("/chat.send")
@SendTo("/topic/public")
public ChatMessage sendMessage(@Payload ChatMessage chatMessage) {
// 可在此处添加消息处理逻辑
return chatMessage;
}
@MessageMapping("/chat.addUser")
@SendTo("/topic/public")
public ChatMessage addUser(@Payload ChatMessage chatMessage,
SimpMessageHeaderAccessor headerAccessor) {
headerAccessor.getSessionAttributes()
.put("username", chatMessage.getSender());
return chatMessage;
}
}
在application.properties中配置:
properties复制# 消息缓冲区大小(字节)
spring.websocket.send-buffer-size=512000
# 消息大小限制(字节)
spring.websocket.message-size-limit=128000
# 心跳间隔(毫秒)
spring.websocket.heartbeat.interval=30000
单机版WebSocket无法水平扩展,必须引入消息代理:
java复制@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableStompBrokerRelay("/topic")
.setRelayHost("rabbitmq-host")
.setRelayPort(61613)
.setClientLogin("admin")
.setClientPasscode("password");
}
}
java复制@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.simpDestMatchers("/app/**").authenticated()
.simpSubscribeDestMatchers("/topic/**").permitAll();
}
}
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 连接频繁断开 | 心跳未配置/Nginx超时 | 调整心跳间隔,Nginx添加proxy_read_timeout 3600s |
| 消息乱序到达 | 客户端未开启ACK | 配置ack: client头信息 |
| 集群环境消息丢失 | 未启用STOMP中继 | 配置RabbitMQ或Redis作为消息代理 |
| 跨域失败 | 未正确处理OPTIONS请求 | 添加CORS过滤器 |
使用SockJS+Stomp.js的示例:
javascript复制const socket = new SockJS('/ws');
const stompClient = Stomp.over(socket);
stompClient.connect({}, (frame) => {
stompClient.subscribe('/topic/public', (message) => {
const msg = JSON.parse(message.body);
// 处理收到的消息
});
// 发送消息
stompClient.send("/app/chat.send", {},
JSON.stringify({content: "Hello", sender: "User1"}));
});
使用JMeter对1000并发用户测试:
| 方案 | 平均延迟 | 吞吐量 | CPU占用 |
|---|---|---|---|
| HTTP轮询(3s) | 1.2s | 120/s | 75% |
| WebSocket | 28ms | 850/s | 32% |
在消息频率超过1次/秒的场景中,WebSocket的优势呈指数级增长。我曾测试过一个每200ms更新数据的仪表盘项目,WebSocket方案节省了92%的网络流量。