WebSocket 实时通信方案设计:从协议选择到断线重连
WebSocket Real-time Communication Design: From Protocol Selection to Reconnection
| Lisa | 2026-08-30T20:39:55
给我们的在线协作工具加了实时通信功能,整个方案从技术选型到上线用了两周。踩了不少坑,尤其是断线重连和消息顺序的问题。
Added real-time communication to our collaboration tool. Two weeks from tech selection to production, with lessons on reconnection and message ordering.
我们有个在线协作工具需要加实时通知功能,用户在一个页面操作后,其他用户的页面要实时更新。评估了 SSE、WebSocket 和长轮询三种方案之后选了 WebSocket。 为什么选 WebSocket SSE:只能服务端到客户端单向推送,我们需要双向通信 长轮询:延迟高、服务器资源消耗大 WebSocket:全双工、延迟低、浏览器支持好 服务端实现 后端用 Spring Boot 的 WebSocket 支持: @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new CollabHandler(), "/ws/collab") .addInterceptors(new JwtHandshakeInterceptor()) .setAllowedOrigins("*"); } } 断线重连(重点) 这是整个方案最难的部分。WebSocket 连接断开的场景很多:网络切换、手机锁屏、服务器重启等。我们的重连策略: class ReconnectableWebSocket { constructor(url) { this.url = url; this.retryCount = 0; this.maxRetry = 10; this.lastEventId = 0; // 用于断线续传 this.connect(); } connect() { this.ws = new WebSocket(\`\${this.url}?lastEventId=\${this.lastEventId}\`); this.ws.onopen = () => { this.retryCount = 0; // 连接成功重置计数 }; this.ws.onclose = (event) => { if (event.code !== 1000) { // 非正常关闭才重连 this.scheduleReconnect(); } }; this.ws.onmessage = (event) => { const msg = JSON.parse(event.data); this.lastEventId = msg.id; // 记录最后收到的消息 ID this.handleMessage(msg); }; } scheduleReconnect() { if (this.retryCount >= this.maxRetry) return; // 指数退避 + 随机抖动 const delay = Math.min(1000 * Math.pow(2, this.retryCount) + Math.random() * 1000, 30000); this.retryCount++; setTimeout(() => this.connect(), delay); } } 消息顺序保证 每条消息都有自增 ID,客户端断线重连时带上 lastEventId,服务端从这个 ID 之后开始推送。同时在服务端用了一个有界队列缓存最近 1000 条消息,超过的就只能靠客户端全量同步了。 经验总结 WebSocket 的"建连"只是万里长征第一步,真正的挑战在于各种异常场景的处理。建议先把断线重连和消息补发的机制设计好,再开始写业务逻辑。
Evaluated SSE, WebSocket, and long polling for real-time collaboration. Chose WebSocket for full-duplex, low latency communication. Key Implementation Spring Boot WebSocket with JWT handshake interceptor. Exponential backoff reconnection with jitter on the client side. Message Ordering Auto-incrementing message IDs with lastEventId for gap detection on reconnect. Server-side bounded queue caching last 1000 messages. Lesson: The WebSocket connection is the easy part. Exception handling (reconnection, message replay) is where the real complexity lives.