什么是 SSE
Server-Sent Events(SSE)是一种服务器端推送技术,它允许服务器发送即时的数据到客户端。SSE 基于 HTTP 协议,使用了一种轻型的长连接机制,从而实现了实时通信。
与 WebSocket 不同之处在于,SSE 没有双向通信的能力,只能由服务器端向客户端发送数据,但因其轻量、易用、无需额外协议支持,所以在某些场景下更为适用。
SSE 实现
一般来说,SSE 是通过简单的 EventSource API 来实现的,这个 API 需要一个指向服务器端的 URL 来建立连接,并且需要在服务器端返回一个 text/event-stream 类型的数据流。
客户端的代码如下所示:
const source = new EventSource('/stream');
source.onmessage = function (event) {
console.log(event.data);
};服务器端代码如下所示:
// www.javascriptcn.com code example
app.get('/stream', (req, res) => {
res.writeHead(200, {
'Connection': 'keep-alive',
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'X-Accel-Buffering': 'no'
});
setInterval(() => {
res.write('data: ' + Date.now() + '\n\n');
}, 1000);
});在示例中,我们使用 setInterval 来模拟每隔一段时间推送实时数据到客户端。
上述代码会让客户端每秒钟接收到服务器端发送的数据,并在控制台上打印出来。在真实场景中,服务器端需要生成类似于 data: message\n\n 这样的数据流来发送信息。
SSE 优化
降低网络流量
在 SSE 中,每一条数据流都会带有一定的开销,这将随着时间的推移变得越来越明显。为了最大化利用带宽和降低流量成本,我们应该寻找并使用各种方法来优化数据流。
一种直观的优化方法是,当服务器端没有数据要发送时,我们可以推送一些注释字符,这样即使没有数据的情况下,数据流也会一直保持连接,从而降低服务器每次发送数据所产生的连接开销。
setInterval(() => {
// 在没有数据时,发送一个注释字符
res.write(': ping\n\n');
}, 10000);避免浏览器缓存
如果你尝试在浏览器中运行示例代码,你会发现浏览器可能会对 SSE 数据流进行缓存,从而导致客户端无法及时接收数据。
我们可以通过在 HTTP 响应头中设置 Cache-Control 来指定数据流不被缓存:
res.writeHead(200, {
'Connection': 'keep-alive',
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'X-Accel-Buffering': 'no'
});减少连接重用
在 SSE 中,服务器端保持连接持续打开的时间越长,连接重用所带来的好处就越明显。如果服务器端在关闭一个连接之前,立即开启另一个链接,那么,这可以减少建立连接所带来的开销,最大化利用带宽资源。
// www.javascriptcn.com code example
const keepAliveTimeout = 5000;
req.setTimeout(0);
req.on('timeout', () => {
res.write(': ping\n\n');
});
res.writeHead(200, {
'Connection': 'keep-alive',
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'X-Accel-Buffering': 'no'
});
let timerId = setInterval(() => {
res.write(': ping\n\n');
}, keepAliveTimeout);
res.on('close', () => {
clearInterval(timerId);
});在上述代码中,我们使用了 req.setTimeout() 来延长连接的时间,并使用 setInterval() 来轮询连接是否关闭。如果连接关闭了,我们就清除定时器并重建一个连接。
总结
从上文中,我们可以看到 SSE 的实现原理和优化方法,同时我们也可以发现,在一些场景中采用 SSE 来实现实时通信是非常合适的。
在更加完整的实现中,我们还可以考虑更多的优化方法,比如使用 HTTP/2 来请求,并使用 Gzip 压缩数据流等等。在实际开发中,我们可以探索更多好的实践方法,来提升 SSE 的使用效率。
Source: FunTeaLearn,Please indicate the source for reprints https://funteas.com/post/647ff06c48841e9894f71c35