服务端事件推送-SSE,来看看吧🤩

2,126 阅读5分钟

本文正在参加「金石计划」

我相信很多同学都遇到过消息推送的场景,因其不需要用户交互而自发产生的行为,所以在具体编码上,需要采用特定的方案

对于这种场景的技术方案,我想大家最常用的无非是下列几种

  • 轮询
  • long polling
  • websocket

但本篇文章的主角并不是它们,而是我最近才了解到的技术方案😂----SSE,本篇文章会着重讲解关于SSE的内容,但是在此之前,还是先简单了解一下前三个技术方案,废话不多说,开搞!

ppx.jpg

轮询

轮询是这里面最简单的一种实现方案,具体原理就是前端通过定时器(setIntervalsetTimeout)的方式,按照固定周期去发送请求,从而去获取最新的数据

Untitled Diagram.drawio.png

这种方式虽然实现起来非常简单,但是有明显的缺陷

  • 存在大量无效的请求,浪费带宽
  • 需要服务器具有很高的实时处理能力,从而快速响应前端的请求
  • 数据的有效性不能很好地保证,因为请求的响应时间是不确定的,可能较早发出的请求,较晚才拿到响应,从而让前端显示过期的数据,需要单独处理

这种方式的唯一好处,我认为就是浏览器兼容性较好,因为核心就是利用定时器,而定时器的功能是可以兼容很老的浏览器,甚至包括IE6😂

inte.jpg

long polling

其实这种方式让我来取名的话,“阻塞式的长连接”感觉更合适,这种方式的核心思路就是在浏览器发出请求后,服务器会保持这个连接,类似于挂起操作,然后去执行业务逻辑,当有结果时,会再通过该连接将响应返回,然后当客户端拿到响应后,会通过该连接再次发起请求,然后不断重复上述步骤

Untitled Diagram.drawio (1).png

简单示例代码如下

async function longPolling() {
  let response = await fetch("/longPolling");
  if (response.status != 200) {
    await longPolling();
  }else {
    doSomething(response)
    await longPolling();
  }
}
longPolling()

可以看到,前端就是利用 递归 的方式去实现long polling的

niubi.jpg

这种方式优缺点都有,先说说优点

  • 不需要客户端发送大量无效的请求,节省带宽
  • 不需要服务端具有较高的实时处理能力
  • 超时自动响应,避免客户端长时间等待
  • 前端实现方式简单,利用 函数递归 就可以实现

缺点如下

  • 需要服务器同时维护多个连接,资源消耗会比较大
  • 由于同一个域名下的tcp连接通道是有限的,这种对连接通道的排他式使用方式,会影响网站的其他资源下载

websocket

我相信对于websocket方案,前端同学应该都是非常熟悉的,它也是实现服务端推送的常用方式之一,本篇文章不想过多去讨论ws,因为不是本文重点,下面给一个简单的使用示例

// Create WebSocket connection.
const socket = new WebSocket('ws://localhost:8080');

// Connection opened
socket.addEventListener('open', function (event) {
    socket.send('Hello Server!');
});

// Listen for messages
socket.addEventListener('message', function (event) {
    console.log('Message from server ', event.data);
});

优点:

  • 可以实现 全双工 通信
  • 支持跨域通信

缺点:

  • 服务器改动比较大,因为需要支持ws或wss协议
  • 需要前端实现鉴活机制,通常是会定时发送心跳包,如果连接出现异常,则触发重新连接的流程
  • 兼容性问题,IE10及以上才支持

SSE

终于到了本文的主角SSE了,它的全称是 server-sent-events,即 服务端事件推送,从它名字我们就可以看出,这个技术的实现方式是借助了 事件驱动 的方式,来实现服务端推送数据的

具体来说,这个方式借助 EventSource 构造出来的实例,实现对服务器推送事件的监听,简单代码示例如下

var evtSource = new EventSource('test.php');

evtSource.onmessage = function(e) {
  console.log(e.data)
}

与websocket相比,有两点不同

  • 有跨域限制
  • 只支持服务端向客户端推送的单向通信

另外需要说明的有两点

  • 服务端与客户端通信的数据格式Content-Type为 text/event-stream
  • 客户端监听的推送事件其实不只是message,可以自定义,其类型根据响应数据里的 event 字段来确定,比如"event: testEvent\ndata:testData\n"这样一段响应内容,其监听的方式就要换成如下形式
  evtSource.addEventListener("testEvent", function(e) {
    console.log(e.data)
  })

对于该方案的优缺点,分别罗列如下

  • 优点
    • 直接复用http协议,无需使用新的协议
    • 支持自定义事件,可以按场景区分不同事件,更方便管理
    • 前端可以使用熟悉的 事件驱动 范式进行编码
    • 浏览器端实现了 断线重连 机制,不需要开发者自己去维护连接活性
  • 缺点
    • 只支持服务端向客户端的单向推送
    • 兼容性不好,IE全系不支持

结语

通过本文的阅读,我们知道了服务端推送数据的各种方式,未来在工作中,如果遇到类似场景,我相信你一定可以合理地选出最适合的方案,毕竟你阅读了我的这篇文章,哈哈🤣

都看到这里啦,如果本篇文章对你有帮助,希望能 点个赞👍 支持下啦,你们的支持才是我最大的动力!😘

R-C.gif