2024年7月21日
约 5 分钟阅读时间
By 麦兜九天 & Tianyang Wang

目录

实时通信技术探索-短轮询、长轮询、WebSocket 和 SSE

一、短轮询(Short Polling)

1.1 技术原理

短轮询是实现实时通信最简单直接的方式,它基于传统的 HTTP 请求 / 响应模式-

  1. 客户端按照固定的时间间隔(如每 1 秒)向服务器发送 HTTP 请求

  2. 服务器收到请求后,立即返回最新的数据(无论数据是否有更新)

  3. 客户端处理响应数据,然后等待下一个时间间隔再次发送请求

短轮询的核心思想是通过频繁的请求来模拟 “实时” 效果,实现简单,兼容性极好,几乎所有浏览器和服务器都支持。

短轮询

1.2 优缺点分析

优点-

  • 实现简单,开发成本低

  • 兼容性极佳,无浏览器限制

  • 不需要特殊的服务器配置

  • 天然支持负载均衡

缺点-

  • 实时性差,取决于轮询间隔

  • 资源浪费严重,即使没有新数据也会频繁发送请求

  • 增加服务器负担,大量无效请求

  • 网络流量大,每个请求都包含 HTTP 头信息

1.3 适用场景

短轮询适用于实时性要求不高、用户量不大的场景,例如-

  • 简单的通知提醒
  • 数据更新频率低的应用
  • 对兼容性要求极高的场景
  • 快速原型开发

1.4 前端实现

function startPolling() {
    const pollInterval = 2000; // 2秒
    
    function poll() {
        fetch('/api/get-updates')
            .then(response => response.json())
            .then(data => {
                updateUI(data);
                setTimeout(poll, pollInterval);
            })
            .catch(error => {
                console.error('Polling error:', error);
                setTimeout(poll, pollInterval);
            });
    }
    
    poll();
}

二、长轮询(Long Polling)

2.1 技术原理

长轮询是对短轮询的改进,旨在减少无效请求,提高实时性-

  1. 客户端向服务器发送 HTTP 请求

  2. 服务器收到请求后,如果没有新数据,不会立即响应,而是将请求挂起

  3. 当有新数据可用时,服务器立即响应客户端的请求

  4. 客户端收到响应后,处理数据,并立即发送一个新的长轮询请求,保持连接

长轮询的核心思想是将多个无效的短轮询合并为一个长连接,只有当有数据更新时才返回响应,减少了网络传输和服务器负载。

alt text

2.2 基于 iframe 的长轮询

基于 iframe 的长轮询是长轮询的另一种实现方案-

  1. 在页面中嵌入一个 iframe,地址指向轮询的服务器地址

  2. 在父页面中放置一个执行函数,比如 execute (data)

  3. 当服务器有内容改变时,会向 iframe 发送一个脚本

  4. 通过发送的脚本,主动执行父页面中的方法,达到推送的效果

2.3 优缺点分析

优点-

  • 实时性比短轮询好,数据更新后能立即推送

  • 减少了无效请求,降低了网络流量

  • 兼容性好,大多数浏览器和服务器都支持

缺点-

  • 可能受限于服务器的连接超时设置

  • HTTP 头信息仍然会带来额外开销

  • 服务器需要维护挂起连接,高并发时资源消耗大

2.4 适用场景

长轮询适用于以下场景-

  • 实时性要求中等的应用

  • 数据更新频率不确定的场景

  • 不能使用 WebSocket 的环境

  • 如社交媒体通知、实时评论系统等

2.5 前端实现

function startLongPolling() {
function longPoll() {
fetch('/api/long-poll')
.then(response => response.json())
.then(data => {
updateUI(data);
longPoll(); // 立即发起下一次请求
})
.catch(error => {
console.error('Long polling error:', error);
setTimeout(longPoll, 1000); // 出错后延迟重试
});
}
longPoll();
}

三、WebSocket

3.1 技术原理

WebSocket 是 HTML5 引入的一种全双工通信协议,它提供了客户端和服务器之间持久的连接,允许双向实时通信-

  1. 客户端通过 HTTP 请求发起 WebSocket 握手,请求头中包含 Upgrade: websocket 和 Connection: Upgrade 等信息

  2. 服务器同意升级协议后,建立 WebSocket 连接

  3. 连接建立后,客户端和服务器可以随时向对方发送数据,无需重新建立连接

  4. 通信使用帧(frame)格式,相比 HTTP 更加轻量

  5. 连接可以通过客户端或服务器主动关闭

WebSocket 的核心优势是建立一次连接后可以双向持续通信,避免了 HTTP 的请求 / 响应模式带来的开销,是真正意义上的实时通信技术。

WebSocket

3.2 优缺点分析

优点-

  • 全双工通信,客户端和服务器可以随时发送数据

  • 持久连接,减少了连接建立的开销

  • 轻量级协议,数据传输效率高

  • 实时性好,延迟低

  • 可以传输二进制数据

缺点-

  • 实现相对复杂

  • 需要服务器支持 WebSocket 协议

  • 负载均衡配置相对复杂

  • 连接状态保持-长时间保持连接可能会导致服务器和客户端都需要维护连接状态

3.3 适用场景

WebSocket 适用于以下场景-

  • 实时聊天应用

  • 实时协作工具(如文档协作)

  • 实时数据监控和仪表盘

  • 高频数据更新的应用

  • 在线游戏

3.4 前端实现

function connectWebSocket() {
const ws = new WebSocket('ws://localhost:8080/ws');
ws.onopen = function() {
console.log('WebSocket connected');
ws.send('Hello Server');
};
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
updateUI(data);
};
ws.onclose = function() {
console.log('WebSocket disconnected');
setTimeout(connectWebSocket, 1000); // 自动重连
};
ws.onerror = function(error) {
console.error('WebSocket error:', error);
};
}

四、SSE(Server-Sent Events)

早期 Web 依赖 HTTP 的请求 - 响应模式,实时性需求(如股票行情、IM 聊天消息)只能通过轮询或长轮询实现,导致高延迟和资源浪费。Comet 技术虽尝试长连接方案,但实现复杂且兼容性差。

随着 ChatGPT 等大模型应用兴起,SSE 因流式输出特性成为大模型交互的首选协议,支持逐词返回的 “打字机效果”,推动技术进一步普及。

SSE

4.2 技术原理

SSE(Server-Sent Events,服务器发送事件)是一种允许服务器主动向客户端推送数据的技术,基于 HTTP 协议-

  1. 客户端通过 HTTP GET 请求建立与服务器的连接

  2. 服务器响应的 MIME 类型为 text/event-stream

  3. 连接建立后保持打开状态,服务器可以随时向客户端发送事件

  4. 数据以特定格式的文本流形式传输

  5. 客户端可以通过关闭连接来终止通信

SSE 的核心特点是单向通信(服务器到客户端),基于标准 HTTP 协议,实现简单,非常适合需要服务器主动推送更新但客户端不需要频繁发送数据的场景。

4.3 优缺点分析

优点-

  • 实现简单,基于 HTTP 协议,不需要特殊协议支持

  • 自动重连机制,连接断开后客户端会自动尝试重连

  • 轻量级协议,数据格式简单

  • 服务器可以发送事件 ID,客户端可以记录最后接收的 ID,重连时可以请求从该 ID 开始的事件

  • 兼容性较好(除 IE 外的大部分浏览器)

缺点-

  • 单向通信,只能服务器向客户端发送数据

  • 只能传输文本数据,二进制数据需要编码

  • 受限于 HTTP 的并发连接数限制(同一域名下通常限制为 6 个连接)

  • 不支持跨域(需要服务器配置 CORS)

4.4 适用场景

SSE 适用于以下场景-

  • 新闻推送、通知提醒

  • 实时数据更新(如股票行情、天气数据)

  • 状态监控(如服务器状态、设备状态)

  • 社交媒体动态流

  • 大模型流式输出(如 ChatGPT 的打字机效果)

4.5 前端实现

function connectSSE() {
    const eventSource = new EventSource('/api/sse');
    
    eventSource.onmessage = function(event) {
        const data = JSON.parse(event.data);
        updateUI(data);
    };
    
    eventSource.addEventListener('custom-event', function(event) {
        const data = JSON.parse(event.data);
        handleCustomEvent(data);
    });
    
    eventSource.onerror = function(error) {
        console.error('SSE error:', error);
        eventSource.close();
    };
}

五、四种实时通信技术的综合对比

5.1 技术特性对比

特性短轮询长轮询WebSocketSSE
通信方式客户端定期请求客户端请求,服务器延迟响应全双工持久连接服务器单向推送
实时性低(取决于轮询间隔)中(有数据时立即响应)高(毫秒级延迟)高(毫秒级延迟)
连接类型短连接,频繁建立长连接,数据推送后关闭持久连接,一直保持持久连接,一直保持
数据方向客户端请求,服务器响应客户端请求,服务器响应双向服务器到客户端
协议HTTPHTTPWebSocketHTTP
开销高(每次请求有 HTTP 头)中(请求头开销,但次数少)低(连接建立后开销小)低(连接建立后开销小)
服务器负载高(大量无效请求)中(挂起连接消耗资源)低(单个连接处理多请求)低(单个连接处理多推送)
实现复杂度简单中等复杂简单
浏览器支持所有浏览器所有浏览器现代浏览器现代浏览器(除 IE)

5.2 SSE vs WebSocket 详细对比

SSE 的优势-

  • 使用 HTTP 协议,现有的服务器软件都支持

  • 属于轻量级,使用简单

  • 默认支持断线重连,WebSocket 需要自己实现

  • 支持自定义发送的消息类型

WebSocket 的优势-

  • 全双工通信,支持双向数据传输

  • 可以传输二进制数据

  • 更低的延迟

  • 更好的跨域支持

六、技术选型建议

6.1 基于场景的选择

  1. 简单场景,兼容性要求极高-选择短轮询
  • 优点-实现最简单,所有浏览器都支持

  • 缺点-实时性差,服务器负载高

  • 适用-内部系统,用户量小,更新频率低的场景

  1. 中等实时性要求,需要广泛兼容-选择长轮询
  • 优点-实时性比短轮询好,兼容性好

  • 缺点-服务器需要维护挂起连接

  • 适用-社交媒体通知,评论系统,配置中心

  1. 高实时性,需要双向通信-选择 WebSocket
  • 优点-全双工,低延迟,高效率

  • 缺点-实现复杂,老旧浏览器不支持

  • 适用-实时聊天,多人在线游戏,协作工具,直播系统

  1. 高实时性,仅需服务器推送-选择 SSE
  • 优点-实现简单,自动重连,效率高

  • 缺点-单向通信,IE 不支持

  • 适用-新闻推送,实时数据监控,股票行情,大模型流式输出

6.2 混合使用策略

在实际项目中,有时可以混合使用这些技术-

  1. 降级策略-优先使用 WebSocket,不支持则降级为长轮询,最后降级为短轮询

  2. 场景分离-不同功能使用最适合的技术,例如聊天用 WebSocket,通知用 SSE

  3. 渐进增强-基础功能用短轮询保证兼容性,高级功能用 WebSocket 提升体验

总结

实时通信技术是现代 Web 应用不可或缺的组成部分,从简单的短轮询到先进的 WebSocket 和 SSE,每种技术都有其独特的适用场景和优缺点。

短轮询实现最简单,但效率最低,适合对实时性要求不高且需要广泛兼容的场景。

长轮询在保持兼容性的同时提高了实时性和效率,是短轮询的良好替代方案,特别适合配置中心等场景。

WebSocket提供了全双工的实时通信能力,适合需要高频双向数据交换的场景,如实时聊天、在线游戏等。

SSE则专注于服务器到客户端的单向推送,实现简单且效率高,特别适合大模型流式输出、实时数据监控等场景。

在实际项目中,开发者应该根据具体的业务需求、性能要求、兼容性考虑和开发成本来选择最合适的实时通信技术。对于复杂的应用场景,可能需要采用多种技术的组合策略,以达到最佳的用户体验和系统性能。