掌握长轮询、短轮询、SSE,愉快与后端打交道(Nest 实现 SSE 接口)

轮询是一种在客户端和服务器之间进行通信的技术,主要用于从服务器获取实时更新数据。根据轮询的机制,可以分为短轮询和长轮询。

短轮询

短轮询是一种简单的轮询方式,客户端以固定的时间间隔向服务器发送请求,不论服务器是否有数据更新。

import axios from 'axios';

function shortPolling() {
  setInterval(() => {
    axios.get('/api/data')
      .then(response => {
        console.log('短轮询数据:', response.data);
        // 处理数据
      })
      .catch(error => {
        console.error('短轮询错误:', error);
      });
  }, 5000); // 每5秒请求一次
}

shortPolling();

适用场景:适合实时性要求不高、需要控制服务器负载的场景。

长轮询

长轮询是一种更加高效的轮询方式。客户端发送请求后,服务器会保持请求开放,直到有新数据可发送或达到超时时间。

import axios from 'axios';

function longPolling() {
  function poll() {
    axios.get('/api/data', {
      timeout: 60000 // 设置长时间的超时时间
    })
      .then(response => {
        console.log('长轮询数据:', response.data);
        // 处理数据
        poll(); // 数据处理完毕后,再次发起长轮询请求
      })
      .catch(error => {
        if (axios.isCancel(error)) {
          console.log('长轮询被取消:', error.message);
        } else {
          console.error('长轮询错误:', error);
        }
        setTimeout(poll, 5000); // 发生错误后,等待5秒再次发起请求
      });
  }

  poll();
}

longPolling();

适用场景:适合需要较高实时性、减少请求次数的场景。
短轮询和长轮询相比,都不如 WebSocket 和 Server-Sent Events(SSE)在实时性和效率上表现好。

WebSocket 和 SSE 通信过程

WebSocket

  • 客户端发起握手:客户端向服务器发送一个特殊的 HTTP 请求,请求中包含 Upgrade: websocket 和 Connection: Upgrade 头部字段,表明客户端希望将通信协议从 HTTP 升级到 WebSocket。
  • 服务器响应握手:如果服务器支持 WebSocket,则会返回一个 HTTP 响应,状态码为 101,同时携带相应的头部字段 Upgrade: websocket 和 Connection: Upgrade,确认协议切换。
  • 数据传输:握手成功后,客户端和服务器可以直接交换文本和二进制数据,不需要像传统的 HTTP 请求那样每次交互都需要发起一个新的请求。
  • 关闭连接:任何一方可以通过发送一个关闭帧来发起关闭连接的握手,该帧包含关闭的原因和状态码。

SSE

SSE 是一种轻量级的协议,允许服务器向客户端推送实时数据。适用于如下场景:

  • 私信通知
  • 股票行情更新
  • 新闻订阅

而基于 HTTP 协议的 Server Send Event 通信过程:

  1. 客户端请求:通过普通的HTTP GET请求,并在头部包含 Accept: text/event-stream。
  2. 服务器响应:保持连接打开,设置响应 Content-Type: text/event-stream,开始发送数据,可以多次发送。
  3. 发送消息:服务器按照 SSE 的格式发送消息,每条消息通常包含一个事件类型(event)、数据(data)和一个可选的 id。消息以两个换行符 \n\n 结尾。例如
data: 第一条消息内容\n\n

或者,如果一条消息包含多行数据,它会这样发送:

data: first line\n
data: second line\n\n

如果指定了事件类型和 id,它们将作为消息的一部分被发送:

id: 1
event: myMessage
data: 第二条消息内容\n\n

4. 客户端处理:在 JS 中,通过创建一个 EventSource 对象并监听它的 onmessage 事件。如果服务器指定了事件类型,例如上面指定了 myMessage,客户端需要监听 myMessage 事件类型以收到数据。
5. 保持连接:如果连接断开,客户端会尝试重新连接。如果服务器提供了 id,客户端会在重连时发送 Last-Event-ID 头部,以便服务器从正确的数据点继续发送。
6. 关闭连接:客户端可以调用 EventSource 对象的 close() 方法关闭连接。服务器也可以通过发送特定消息指示关闭。

CI/CD 平台的实时日志打印,ChatGPT 的分段加载回答,通常基于 SSE 实现。
SSE 通常用于传输文本数据,不推荐用于传输大量二进制数据。

Nest 实现 SSE 接口

我们实现一下,创建 nest 项目:

npx nest new sse-project

运行:

npm run start:dev

在 AppController 添加一个 stream 接口:
掌握长轮询、短轮询、SSE,愉快与后端打交道(Nest 实现 SSE 接口)
使用 @Sse() 装饰器来标记为 SSE 端点。
返回的是一个 Observable 对象,然后内部用 observer.next 返回消息。
sse1 我们先返回了 ‘hello’,三秒后返回了 ‘world’。

我们支持下跨域:
掌握长轮询、短轮询、SSE,愉快与后端打交道(Nest 实现 SSE 接口)

React 接收 SSE 接口数据

写一个前端页面,创建 react 项目:

npx create-react-app --template=typescript sse-project-frontend

在 App.tsx 里写如下代码:
掌握长轮询、短轮询、SSE,愉快与后端打交道(Nest 实现 SSE 接口)
通过 new EventSource 这个原生 API,监听上面的 onmessage 回调函数,获取 sse 接口的响应。

将渲染 App 外层的严格模式注释,它会导致多余的渲染。
掌握长轮询、短轮询、SSE,愉快与后端打交道(Nest 实现 SSE 接口)
执行 npm run start。

因为 3000 端口被 nest 应用占用了,react 应用跑在 3001 端口。
点击 event1 按钮:
掌握长轮询、短轮询、SSE,愉快与后端打交道(Nest 实现 SSE 接口)
控制台先打印 ‘hello’,三秒后打印 ‘world’,我们可以取里面的 data 属性拿到最终数据:
掌握长轮询、短轮询、SSE,愉快与后端打交道(Nest 实现 SSE 接口)

点击 even2 按钮:
掌握长轮询、短轮询、SSE,愉快与后端打交道(Nest 实现 SSE 接口)
控制台不断打印:
掌握长轮询、短轮询、SSE,愉快与后端打交道(Nest 实现 SSE 接口)
表明我们不断收到服务端推送的数据。

响应的 Content-Type 是 text/event-stream:
掌握长轮询、短轮询、SSE,愉快与后端打交道(Nest 实现 SSE 接口)
然后在 EventStream 可以看到每次收到的消息:
掌握长轮询、短轮询、SSE,愉快与后端打交道(Nest 实现 SSE 接口)

SSE 日志实时推送

tail -f 命令可以实时看到文件的最新内容:
我们可以通过 child_process 模块的 exec 来执行这个命令,监听 log 文件改动,返回给客户端改动内容:
掌握长轮询、短轮询、SSE,愉快与后端打交道(Nest 实现 SSE 接口)
./log 指的是当前工作目录下名为 log 的文件。在这里,. 表示当前工作目录。
可以输入 node 然后再输入 process.cwd() 来查看当前的工作目录。
前端连接这个新接口:
掌握长轮询、短轮询、SSE,愉快与后端打交道(Nest 实现 SSE 接口)
输入 111 保存,再输入 222 保存:
掌握长轮询、短轮询、SSE,愉快与后端打交道(Nest 实现 SSE 接口)
控制台打印两条信息:
掌握长轮询、短轮询、SSE,愉快与后端打交道(Nest 实现 SSE 接口)
浏览器收到了实时的日志,可以对 data 属性值进行 JSON.parse()
很多构建日志都是通过 SSE 的方式实时推送的。

原文链接:https://juejin.cn/post/7348327193061277732 作者:云牧

(0)
上一篇 2024年3月21日 上午10:32
下一篇 2024年3月21日 上午10:42

相关推荐

发表回复

登录后才能评论