利用sse 实现数据实时更新

应用场景

在目前项目中,经常有好多交互信息需要事实的更新, 比如说我们的echarts数据大屏就需要这样 实时数据的时候。我们可以采用websocket,但是呢我感觉连接成本会增加,而基于sse的话

Server Send Event(SSE)概念

Server Send Event (SSE)是HTML5的API,用于在服务器和客户端之间实时推送数据流。它与WebSocket不同的是,SSE是一个单工通信,是服务端向客户端定向的推送消息。

Server Send Event协议

SSE协议本质上就是一个Http的get请求,当然也是支持Https,服务端在接到该请求后,返回状态。同时请求头设置也变为流形式。

txt 复制代码
Content-Type: text/event-stream, 
Cache-Control: no-cache, 
Connection: keep-alive 

客户端实现

客户端通过EventSource对象与服务器的一个http get请求建立长连接。

new EventSource()建立与服务端的连接。

onmessage()用来监听服务端发来的消息。

onerror()用来监听连接的错误。

客户端代码如下。

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h2>Server Send Event测试</h2>
    <ul id="ul"></ul>
    <script>
      const source = new EventSource("http://localhost:3000/api/sse");
      //   sse.addEventListener("test", (event) => {
      //     console.log(event);
      //     console.log(event.data);
      //   });
      //   sse.onerror = function (error) {
      //     console.error("Error occurred:", error);
      //   };
      source.onopen = () => {
        console.log("连接成功");
      };
      source.onmessage = (res) => {
        console.log("获得的数据是:" + res.data);
        var ulDoom = document.getElementById("ul");
        var child = document.createElement("li");
        child.innerHTML = res.data;
        ulDoom.append(child);
      };
      source.onerror = (err) => {
        console.log(err);
      };
    </script>
  </body>
</html>

服务端实现

PassThrough将字节转成流形式。

router.get('/sse', async (ctx, next),创建一个get请求(也就是路由,用来响应)。

Content-Type': 'text/event-stream 这里必须将请求设置成流的形式 ctx.body = stream;将消息以流的形式返回给客户端

js 复制代码
const Koa = require("koa");
const app = new Koa();
const { PassThrough } = require("stream");
const router = require("koa-router")({
  prefix: "/api",
}); // 注意这里最后是函数调用
const cors = require("koa-cors");

router.get("/hello", async (ctx, next) => {
  ctx.body = { message: "Hello, World!" }; // 返回一个JSON对象
});

 
const sendMessage = async (stream) => {
  const data = [
    "hello,jerry",
    "have not see you for a long time",
    "how are you",
    "what are you doing now ",
    "i am missing you",
  ];

  // 循环上面数组: 推送数据、休眠 2 秒
  for (const value of data) {
    stream.write(`data: ${JSON.stringify(value)}\n\n`); // 写入数据(推送数据)
    await new Promise((resolve) => setTimeout(resolve, 2000));
  }

  // 结束流
  // stream.end();
};

// SSE 路由处理
router.get("/sse", async (ctx, next) => {
  // 设置响应头
  ctx.set({
    "Content-Type": "text/event-stream",
    "Cache-Control": "no-cache",
    Connection: "keep-alive",
  });

  // 2. 创建流、并作为接口数据进行返回
  const stream = new PassThrough();
  ctx.body = stream;
  ctx.status = 200;

  // 3. 推送流数据
  sendMessage(stream, ctx);
});

app.use(
  cors({
    origin: function (ctx) {
      // 允许指定域名或者所有源('*')
      return "*";
    },

    credentials: true, // 允许携带cookie
    allowMethods: ["GET", "POST", "PUT", "DELETE"], // 允许的方法列表
    allowHeaders: ["Content-Type", "Authorization"], // 允许的请求头列表
  })
);
app.use(router.routes());

// 监听端口
const PORT = 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

运行代码的结果如下:

Server-Sent Events (SSE) 优点:

  1. 简单性 :基于HTTP/HTTPS协议,实现相对简单,只需设置特定的MIME类型(text/event-stream)并保持一个长连接。
  2. 兼容性:大多数现代浏览器支持SSE,不需要额外的客户端库。
  3. 单向推送:对于仅需要服务器到客户端的数据更新,比如股票报价、新闻更新等场景很适合。
  4. 自动重连:如果连接中断,SSE会尝试重新建立连接。
  5. 可发送多事件序列:服务器可以连续发送多个独立事件。

SSE 缺点:

  1. 单向通信:只能由服务器向客户端发送数据,不支持客户端主动向服务器发送消息。
  2. 网络不稳定:虽然具备重连机制,但在网络波动频繁的情况下,可能造成更新延迟或丢失。
  3. 流量控制:相较于WebSocket,缺乏更精细的流量控制功能。

WebSocket 优点:

  1. 双向通信:允许服务器与客户端之间进行全双工通信,实时性强。
  2. 低延迟:一旦建立连接后,数据传输速度快且延迟低,适用于实时聊天、游戏等应用。
  3. 持续连接:通过TCP持久连接,无需轮询,资源消耗较低。
  4. 灵活的消息格式:支持自定义消息结构,不受限于SSE规定的格式。
  5. 更好的性能:在大量并发和高频通信场景下,性能表现优于长轮询方案。

WebSocket 缺点:

  1. 复杂性:实现和配置通常比SSE复杂,需要客户端和服务端都支持WebSocket协议。
  2. 兼容性:尽管主流浏览器普遍支持WebSocket,但旧版或某些特殊环境下可能存在兼容问题。
  3. 开销:每条WebSocket连接都需要维持一个独立的TCP连接,对服务器资源需求较高。

选择使用SSE而不是WebSocket的原因通常在于以下几点:

  • 如果只需要单向通信,而不需要客户端主动发送数据给服务器。
  • 对于简单的数据推送场景,SSE实现更为简洁易用。
  • 当项目中已有的基础设施更加适应基于HTTP的服务端推送。

然而,在要求双向实时通信、交互频繁或者对延迟要求较高的应用中,WebSocket通常是更合适的选择。

相关推荐
花花鱼2 分钟前
vue3 基于element-plus进行的一个可拖动改变导航与内容区域大小的简单方法
前端·javascript·elementui
k09336 分钟前
sourceTree回滚版本到某次提交
开发语言·前端·javascript
EricWang135827 分钟前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
September_ning27 分钟前
React.lazy() 懒加载
前端·react.js·前端框架
web行路人37 分钟前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架
超雄代码狂1 小时前
ajax关于axios库的运用小案例
前端·javascript·ajax
长弓三石1 小时前
鸿蒙网络编程系列44-仓颉版HttpRequest上传文件示例
前端·网络·华为·harmonyos·鸿蒙
小马哥编程1 小时前
【前端基础】CSS基础
前端·css
嚣张农民2 小时前
推荐3个实用的760°全景框架
前端·vue.js·程序员
周亚鑫2 小时前
vue3 pdf base64转成文件流打开
前端·javascript·pdf