使用Event Source实现服务端数据实时推送

一、“实时通信”的常规套路

当涉及后端向前端提供实时实时数据时,按照常规套路,我们一般有如下选择:

1、轮询

它通过定期向服务器发送请求获取最新数据的方式来实现实时数据更新,虽然实现比较简单,但是弊端也很明显:

  • 浪费带宽和资源:轮询会定期向服务器发送请求,即使没有数据更新,也会浪费带宽和服务器资源。特别是在高并发场景下,轮询会给服务器带来很大的压力,可能导致服务器负载过高,响应速度变慢,甚至宕机。
  • 延迟高:轮询是定期向服务器请求数据,而不是实时获取数据。如果轮询时间间隔过长,会导致数据更新的延迟非常高。如果轮询时间间隔过短,会导致服务器压力增大,甚至无法承受。
  • 实时性差:轮询是定期请求数据,无法实时获取最新数据。如果数据更新频率非常高,轮询可能无法及时获取最新数据,导致实时性差。
  • 不适合大规模并发场景:轮询是单向请求,不适合大规模并发场景。在这种场景下,客户端向服务器发送大量请求,可能导致服务器响应速度变慢,甚至宕机。

2、长连接

通过在客户端和服务端之间建立一直保持的连接,实现实时通信,虽然这种方式有很多优点,但也存在一些弊端:

  • 连接维持成本高:长连接需要一直保持连接,占用服务器资源,即使在数据传输过程中没有实际数据传输。这会带来连接维持成本高的问题,特别是在高并发场景下,可能导致服务器资源瓶颈。
  • 可靠性问题:长连接一旦建立,需要一直保持连接,但是在网络不稳定、断网等情况下,长连接可能会断开。这会导致数据传输失败,需要进行断线重连等操作。
  • 安全问题:长连接需要一直保持连接,可能会导致一些安全问题。例如,恶意攻击者可能会利用长连接发送大量的请求,占用服务器资源,或者窃取数据。
  • 兼容性问题:长连接可能不被一些老旧的浏览器或操作系统支持,导致应用程序的兼容性问题。

3、Websocket

它通过在客户端和服务端之间建立全双工通信的长连接来实现实时通信,虽然它有颇多优点,但是缺点也很明显:

  • 连接维持成本高:WebSocket的连接是一直保持的,需要一直占用服务器资源,即使在数据传输过程中没有实际数据传输。这会带来连接维持成本高的问题,特别是在高并发场景下,可能导致服务器资源瓶颈。
  • 安全问题:WebSocket通常使用加密的SSL/TLS连接,但是仍然存在一些安全问题。例如,恶意攻击者可能会利用WebSocket连接发送大量的请求,占用服务器资源,或者窃取数据。
  • 兼容性问题:虽然现代浏览器都支持 WebSocket 技术,但是一些老旧的浏览器可能不支持,或者支持不完整,这可能导致应用程序的兼容性问题。
  • 负载均衡问题:WebSocket的长连接需要占用服务器资源,特别是在高并发场景下,可能会导致服务器负载不均衡。为了解决这个问题,需要采取一些负载均衡措施,如使用多台服务器、使用反向代理等。

二、什么是EventSource

EventSource是HTML5中提供的一种新的服务器端推送技术,可以让客户端浏览器直接从服务器端接收实时的数据更新。EventSource协议是一个基于 HTTP的协议,其与 AJAX 和WebSocket不同,EventSource传输数据的方式是单向的,仅从服务端到客户端,而不需要客户端到服务端交互。主要有以下优点:

  • 简单:EventSource API简单易用,开发人员可以轻松地使用它来实现实时数据推送。
  • 可靠:EventSource建立的连接是持久连接,而不是短暂的轮询,因此可以更可靠地传输数据。
  • 高效:EventSource使用 HTTP 协议,可以利用现有的基础设施,并避免了 WebSocket 的握手和帧头等开销。
  • 可扩展:EventSource可以通过扩展HTTP头来支持自定义的元数据,并可以使用JSON、XML等格式来传输数据。
  • 兼容性好:大部分主流浏览器都已经支EventSource技术,比如Chrome、Firefox、Safari、Opera和Edge等等,对于IE等不支持的浏览器也可以通过polyfill来补全。

三、如何使用EventSource

要使用EventSource,客户端需要创建一个新的EventSource对象,并将服务器端的URL作为参数传递。服务器端需要将数据格式化为一系列事件,并将其发送到客户端。

下面是一个简单的示例代码:

const eventSource = new EventSource('/updates');
eventSource.addEventListener('message', function(event) {
  console.log(event.data);
});

这里通过创建EventSource对象,发送GET请求到/updates,并在监听到服务器推送的消息后,调用回调函数onmessage处理消息。

服务端的代码可以使用任何变成语言实现,但需要遵循EventSource数据规范。服务器端需要将数据格式化为一系列事件,然后将其发送到客户端,事件可以包含以下字段:

  • event 属性,表示事件类型;
  • data 属性,表示事件数据,采用默认的UTF-8编码。

事件的一般格式如下:

event: event_name\n
data: event_data\n
id: event_id\n

注意:每个事件必须以\n结束,否则客户端无法正常解析事件。

下面是一个使用Node和Express实现的服务端示例:

const express = require('express');
const app = express();
app.get('/updates', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  setInterval(function() {
    res.write('data: Hello, world!\n\n');
  }, 1000);
});
app.listen(3000);

四、应用场景

EventSource的应用场景十分广泛,以下是一些常见的场景:

  • 聊天室:使用EventSource可以实现实时聊天功能。当用户在聊天室中发送消息时,服务器可以将消息发送到所有在线用户,而无需客户端轮询服务器。这可以大大减少服务器负载,提高用户体验。
  • 股票报价:股票价格变化频繁,需要及时更新。使用EventSource可以实现实时股票报价功能,当股票价格发生变化时,服务器可以向客户端发送最新的价格信息,而无需客户端轮询服务器。
  • 实时通知:当某个事件发生时,服务器可以向客户端发送通知消息,如新的电子邮件、新的社交媒体更新等。这可以让用户及时了解最新信息,并提高用户体验。

五、兼容性

EventSource的兼容性还是不错的,大部分主流浏览器都支持,对于不支持的浏览器也可以通过polyfill来补全。

  • 支付宝二维码 支付宝
  • 微信二维码 微信

本文地址: /push-data-with-eventsource.html

版权声明: 本文为原创文章,版权归 逐梦个人博客 所有,欢迎分享本文,转载请保留出处!

相关文章