你点赞了吗?你关注了吗?每天分享干货好文。
高并发解决方案与架构设计。
海量数据存储和性能优化。
通用框架/组件设计与封装。
如何设计合适的技术架构?
如何成功转型架构设计与技术管理?
在竞争激烈的大环境下,只有不断提升核心竞争力才能立于不败之地。
公众号留言【我要晋级】,一对一指导,带你晋级。
一、实时通信技术选型:SSE vs WebSocket
在实时数据推送场景中(如股票行情、实时日志、消息通知),SSE(Server-Sent Events) 和 WebSocket 是最常用的两种方案。它们的核心差异如下:
特性 | SSE | WebSocket |
---|---|---|
协议 | 基于 HTTP(单向) | 基于 TCP(全双工) |
数据格式 | 仅文本(UTF-8) | 文本/二进制 |
连接方式 | 长连接,自动重连 | 持久化双向连接 |
浏览器兼容性 | 除 IE 外主流浏览器均支持 | 主流浏览器均支持 |
复杂度 | 低(无需额外协议) | 高(需处理握手、心跳) |
适用场景 | 服务器向客户端单向推送 | 双向实时交互(如聊天室) |
二、SSE 核心原理
1. 通信流程
1. 客户端发起 HTTP 请求,Header 中指定 Accept: text/event-stream
2. 服务器保持连接打开,持续发送事件流
3. 数据格式为纯文本,按 event: data\n\n 分隔
4. 客户端通过 EventSource API 接收并处理事件
2. 数据格式规范
// 单条事件
event: priceUpdate\n // 事件类型(可选)
data: {"symbol":"BTC","price":50000}\n\n
// 多行数据
data: 第一行内容\n
data: 第二行内容\n\n
// 重连时间(毫秒)
retry: 3000\n\n
三、Spring Boot 服务端实现
1. 添加依赖
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2. 创建 SSE 控制器
@RestController
@RequestMapping("/sse")
public class SseController {
private static final Map<String, SseEmitter> emitters = new ConcurrentHashMap<>();
// 创建 SSE 连接
@GetMapping("/connect")
public SseEmitter connect(@RequestParam String clientId) {
SseEmitter emitter = new SseEmitter(60 * 60 * 1000L); // 超时时间1小时
emitters.put(clientId, emitter);
emitter.onCompletion(() -> emitters.remove(clientId));
emitter.onTimeout(() -> emitters.remove(clientId));
emitter.onError((e) -> emitters.remove(clientId));
return emitter;
}
// 推送消息到指定客户端
@PostMapping("/send")
public void sendMessage(@RequestParam String clientId,
@RequestParam String message) {
SseEmitter emitter = emitters.get(clientId);
if (emitter != null) {
try {
emitter.send(SseEmitter.event()
.data(message)
.id(UUID.randomUUID().toString())
.reconnectTime(5000));
} catch (IOException e) {
emitters.remove(clientId);
}
}
}
}
3. 模拟数据推送
@Scheduled(fixedRate = 3000)
public void pushStockPrices() {
emitters.forEach((clientId, emitter) -> {
String price = "BTC: " + (50000 + new Random().nextInt(1000));
sendMessage(clientId, price);
});
}
四、Vue 客户端实现
1. 创建 EventSource 连接
<template>
<div>
<h3>实时价格:</h3>
<div>{{ message }}</div>
</div>
</template>
<script>
export default {
data() {
return {
message: '',
eventSource: null
};
},
mounted() {
this.connectSSE();
},
methods: {
connectSSE() {
const clientId = 'user_' + Math.random().toString(36).substr(2, 9);
this.eventSource = new EventSource(`http://localhost:8080/sse/connect?clientId=${clientId}`);
this.eventSource.onmessage = (event) => {
this.message = event.data;
};
this.eventSource.onerror = (error) => {
console.error('SSE 连接错误:', error);
// 自动重连
setTimeout(() => this.connectSSE(), 3000);
};
}
},
beforeUnmount() {
if (this.eventSource) {
this.eventSource.close();
}
}
};
</script>
2. 处理自定义事件类型
// 监听特定事件
this.eventSource.addEventListener('priceUpdate', (event) => {
this.message = JSON.parse(event.data);
});
五、与 WebSocket 的代码对比
1. WebSocket 服务端(Spring Boot)
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new StockWebSocketHandler(), "/ws/stocks");
}
class StockWebSocketHandler extends TextWebSocketHandler {
private List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) {
sessions.add(session);
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
// 处理客户端消息(双向通信)
sessions.forEach(s -> s.sendMessage(message));
}
}
}
2. WebSocket 客户端(Vue)
const socket = new WebSocket('ws://localhost:8080/ws/stocks');
socket.onmessage = (event) => {
this.message = event.data;
};
// 发送消息到服务端
socket.send('请求最新价格');
六、生产环境优化建议
1. SSE 优化
-
心跳机制:定期发送注释行(
:keepalive\n\n
)防止连接超时 -
负载均衡:使用 Nginx 配置
proxy_buffering off
避免缓冲 -
安全加固:结合 JWT 验证客户端身份
2. WebSocket 优化
-
STOMP 协议:使用 STOMP over WebSocket 管理消息路由
-
心跳检测:客户端/服务端定期发送 Ping/Pong 帧
-
集群支持:通过 Redis Pub/Sub 实现多节点消息广播
七、总结:如何选择 SSE 和 WebSocket?
-
选择 SSE 的场景:
-
只需服务器到客户端的单向推送
-
要求快速实现、低复杂性
-
兼容现有 HTTP 基础设施(如认证、防火墙)
-
-
选择 WebSocket 的场景:
-
需要双向实时交互(如聊天、协作编辑)
-
传输二进制数据(如文件、视频流)
-
对延迟极度敏感(如在线游戏)
-
核心结论:SSE 是轻量级单向推送的首选,WebSocket 适合全双工复杂场景。根据业务需求合理选择,甚至可组合使用(如 SSE 推送通知,WebSocket 处理交互)。
架构设计之道在于在不同的场景采用合适的架构设计,架构设计没有完美,只有合适。
在代码的路上,我们一起砥砺前行。用代码改变世界!
精彩推荐>>>