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

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

短轮询

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

javascript 复制代码
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();

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

长轮询

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

javascript 复制代码
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 结尾。例如
kotlin 复制代码
data: 第一条消息内容\n\n

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

arduino 复制代码
data: first line\n
data: second line\n\n

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

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

CI/CD 平台的实时日志打印,ChatGPT 的分段加载回答,通常基于 SSE 实现。

SSE 通常用于传输文本数据,不推荐用于传输大量二进制数据。

Nest 实现 SSE 接口

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

bash 复制代码
npx nest new sse-project

运行:

bash 复制代码
npm run start:dev

在 AppController 添加一个 stream 接口:

使用 @Sse() 装饰器来标记为 SSE 端点。

返回的是一个 Observable 对象,然后内部用 observer.next 返回消息。

sse1 我们先返回了 'hello',三秒后返回了 'world'。

我们支持下跨域:

React 接收 SSE 接口数据

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

bash 复制代码
npx create-react-app --template=typescript sse-project-frontend

在 App.tsx 里写如下代码:

通过 new EventSource 这个原生 API,监听上面的 onmessage 回调函数,获取 sse 接口的响应。

将渲染 App 外层的严格模式注释,它会导致多余的渲染。

执行 npm run start。

因为 3000 端口被 nest 应用占用了,react 应用跑在 3001 端口。

点击 event1 按钮:

控制台先打印 'hello',三秒后打印 'world',我们可以取里面的 data 属性拿到最终数据:

点击 even2 按钮:

控制台不断打印:

表明我们不断收到服务端推送的数据。

响应的 Content-Type 是 text/event-stream:

然后在 EventStream 可以看到每次收到的消息:

SSE 日志实时推送

tail -f 命令可以实时看到文件的最新内容:

我们可以通过 child_process 模块的 exec 来执行这个命令,监听 log 文件改动,返回给客户端改动内容:

./log 指的是当前工作目录下名为 log 的文件。在这里,. 表示当前工作目录。

可以输入 node 然后再输入 process.cwd() 来查看当前的工作目录。

前端连接这个新接口:

输入 111 保存,再输入 222 保存:

控制台打印两条信息:

浏览器收到了实时的日志,可以对 data 属性值进行 JSON.parse()

很多构建日志都是通过 SSE 的方式实时推送的。

相关推荐
哆啦A梦158819 分钟前
商城后台管理系统 03 登录布局
javascript·vue.js·elementui
曼巴UE544 分钟前
UE FString, FName ,FText 三者转换,再次学习,官方文档理解
服务器·前端·javascript
selt7911 小时前
Redisson之RedissonLock源码完全解析
android·java·javascript
行走的陀螺仪2 小时前
高级前端 Input 公共组件设计方案(Vue3 + TypeScript)
前端·javascript·typescript·vue·组件设计方案
一颗不甘坠落的流星2 小时前
【Antd】基于 Upload 组件,导入Json文件并转换为Json数据
前端·javascript·json
LYFlied2 小时前
Vue2 与 Vue3 虚拟DOM更新原理深度解析
前端·javascript·vue.js·虚拟dom
Lucky_Turtle2 小时前
【Node】npm install报错npm error Cannot read properties of null (reading ‘matches‘)
前端·npm·node.js
小飞侠在吗2 小时前
vue shallowRef 与 shallowReacitive
前端·javascript·vue.js
惜分飞3 小时前
sql server 事务日志备份异常恢复案例---惜分飞
前端·数据库·php