NestJS 编写 SSE 接口推送数据

news2024/11/26 2:34:46

做项目的时候遇到了顺便就记一下相关的内容。

SSE

Server-Sent Events(SSE)技术,它是一种用于实现服务器向客户端实时推送数据的Web技术。SSE基于HTTP协议,允许服务器将数据以事件流(Event Stream)的形式发送给客户端。客户端通过建立持久的HTTP连接,并监听事件流,可以实时接收服务器推送的数据。

SSE 和 Websocket

SSE 常常被用在与 Websocket 比较,但是他们的底层和使用有很大的区别,具体如下:

  • SSE是服务器向客户端的单向通信,也就是连接完成后,只有后端可以向前端推送数据,前端被动的接收数据。而WebSocket是双向通信,后端和前端之间可以进行实时的双向数据交换
  • SSE 使用基于HTTP 协议的长连接,通过的 HTTP 请求和响应来建立连接;而WebSocket 使用基于 TCP 的协议,通过建立 WebSocket 连接来实现双向通信。
  • SSE 长用在实时场景中,比如事实更新价格和数据,实时获取服务器运行状态、日志等信息等。而 WebSocket 多用在双向通信的场景,比如多人聊天、协作编辑等

SSE 的原理

SSE在服务器和客户端之间打开一个单向通道,服务器响应的不是一次性的数据包,而是 text/event-stream 类型的数据流信息,在有数据变更时从服务器流式传输到客户端。
在这里插入图片描述

SSE 的后端代码(Nest.js)

因为项目是 nest.js 的所以这里就用 nest.js 来写一个例子,框架搭建过程就省略了,用的是基础的脚手架,以下的 SSE 的核心代码:

  • 首先 nestjs 已经给我们封装好了 Sse 请求,我们只需要使用@Sse 装饰器填写对应的路由就可以使用它了
import { Controller, Get, Header, Sse } from '@nestjs/common';
import { AppService } from './app.service';
import { Observable, interval, map } from 'rxjs';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}
  @Sse('/sse')
  sse(): Observable<any> {
    return interval(1000).pipe(map((_) => ({ data:'hello world' } )));
  }
}
  • 当然你这样写也是可以的,因为SSE 请求本质也是 HTTP GET 请求,只不过 Content-Type 变成了 text/event-stream。
  @Get('/sse')
  @Sse()
  @Header('Content-type', 'text/event-stream')
  sse(): Observable<any> {
    return interval(1000).pipe(map((_) => ({ data: 'hello world2' })));
  }
  • 这里我们发送的数据用到了 RxJS 库的可观察的对象(Observable) ,它采用了观察者模式设计,主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己。可观察对象是惰性的,只有被订阅后才会执行。其中 Interval 函数的作用是每隔一段时间发出一个数值,你可以可以使用 RxJS 库的其他函数来实现内容的推送,只需要返回的内容是一个 Observable 对象即可实现 SSE 接口功能。
  • 关于这部分的详细内容可以自行查阅 RxJS 库
// 间隔一秒发送数据
return interval(1000).pipe(map((_) => ({ data: 'hello world' })));
// 订阅后推送
const ob$ = new Observable((subscriber) => {
  subscriber.next('hello')
  subscriber.next('world')
  subscriber.complete()
})
return ob$

SSE 的前端代码(React.js)

前端采用的是 React 来编写,我们需要通过 EventSource 函数来实现,它的作用是打通与一个 SSE 接口的连接,之后就可以一直接收其数据了:

const eventSource = new EventSource("http://localhost:3000/sse");
function Test() {
  const [cnt, useCnt] = useState("");
  eventSource.onmessage = ({ data }) => {
    useCnt(cnt + data);
  };
  return (
    <div>
        {cnt}
    </div>
  );
}

其中 message 事件是收到信息时触发的,可以通过以下两种方式来触发

eventSource.addEventListener("message", (event) => {});
eventSource.onmessage = (event) => {};

其中 open 事件是连接建立时触发的

eventSource.addEventListener("open", (event) => {});
eventSource.onopen = (event) => {};

其中 error 事件是发生异常时触发的

eventSource.addEventListener("error", (event) => {});
eventSource.onerror = (event) => {};

当我们离开页面不再需要订阅这个接口时,我们可以通过 close 方法来关闭这个连接

eventSource.close();

一般情况下我们在场景中需要做的是在进入页面时连接 SSE,退出页面时关闭连接,所以我们使用 useEffect 来解决这个问题:

let eventSource: EventSource | null = null;
useEffect(() => {
  eventSource = new EventSource("http://localhost:3000/sse");
  eventSource.onmessage = ({ data }) => {
    console.log(data)
  };
  return (() => {
    eventSource && eventSource.close();
  })
},[])

SSE 存在的问题

  • 由于SSE依赖于HTTP长连接,如果连接数量过多,可能会导致服务器资源不足。
  • 虽然大多数现代浏览器都支持SSE,但仍然有一些旧版本的浏览器不支持。在使用SSE时,要确保您的目标客户端支持SSE,或者提供备用的实时数据推送机制。
  • SSE无法发送二进制数据,只能发送 UTF-8 编码的文本。如果应用程序需要发送二进制数据,就需要使用 Websocket。所以SSE一般多用于推送数据信息给前端,也不用在下载传输文件等内容上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/753793.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习(29)—— DETR

深度学习&#xff08;29&#xff09;—— DETR DETR代码欢迎光临Jane的GitHub&#xff1a;在这里等你 看完YOLO 之后&#xff0c;紧接着看了DETR。作为Transformer在物体检测上的开山之作&#xff0c;虽然他的性能或许不及其他的模型&#xff0c;但是想法是OK的。里面还有一些…

数据结构day1(2023.7.13)

一、Xmind整理&#xff1a; 二、课上练习&#xff1a; 练习1&#xff1a;static&#xff08;全局变量、局部变量作用域&#xff09; int a0;//全局变量 生命周期和作用于都是从定义开始到整个文件结束 void fun() { int b0;//局部变量 static int c0;//局部变量 作用于&#x…

智头条|第25届中国建博会(广州)成功举行,马斯克组建xAI公司

行业动态&#xff1a; 第25届中国建博会&#xff08;广州&#xff09;成功举行 7月8日至11日期间&#xff0c;2023中国建博会(广州)暨首届广州卫博会在广州如火如荼地进行。本届展会以“冠军企业首秀平台”为定位&#xff0c;以“建装理想家&#xff0c;服务新格局”为主题&a…

我的创作纪念日——创作的第2048天

创作机缘 今天收到私信&#xff0c;在CSDN已经7年码龄&#xff0c;创作2048天了&#xff0c;刚开始写作的时候似乎还是在大二&#xff0c;那个懵懂无知的年纪&#xff0c;也是在那个时候开始接触开发&#xff0c;接触编程。 之后便是无尽的探索与尝试&#xff0c;没有明确的发…

DuiLib的消息传递机制

前言 学会了怎么写XML文件&#xff0c;但是我还是不知道怎么实现各个控件之间的消息传递。于是我对源代码好好研究了一下&#xff0c;发现duilib作为一个界面库有自己独立的封装的窗口类,也就是WindowsImplBase。 在这个类中&#xff0c;实现对windows窗口传过来的消息的处理…

【每日一题】2673. 使二叉树所有路径值相等的最小代价

【每日一题】2673. 使二叉树所有路径值相等的最小代价 2673. 使二叉树所有路径值相等的最小代价题目描述解题思路 2673. 使二叉树所有路径值相等的最小代价 题目描述 给你一个整数 n 表示一棵 满二叉树 里面节点的数目&#xff0c;节点编号从 1 到 n 。根节点编号为 1 &#…

前端day06笔记

数组遍历 for let i in 数组名 函数 没有return返回的是underfined var是全局作用域 匿名函数 具名函数 值传递 引用传递 arguments:接实参 箭头函数 递归 闭包 对象的增删改查 对象的遍历 数组对象 获取数组对象 内置对象 从2-10

保险企业如何做好数据安全合规与敏感数据保护

监管部门多次“重拳出击”&#xff0c;保险企业如何做好敏感数据保护工作&#xff1f; 继《个人信息保护法》、《中华人民共和国消费者权益保护法》、《中国银保监会办公厅关于印发银行保险机构信息科技外包风险监管办法的通知》、《互联网保险业务监管办法》等相关法规之后&am…

CNN从搭建到部署实战(pytorch+libtorch)

模型搭建 下面的代码搭建了CNN的开山之作LeNet的网络结构。 import torchclass LeNet(torch.nn.Module):def __init__(self):super(LeNet, self).__init__()self.conv torch.nn.Sequential(torch.nn.Conv2d(1, 6, 5), # in_channels, out_channels, kernel_sizetorch.nn.Sig…

mysql 第三章

目录 1.索引 2.事务 3.总结 1.索引 2.事务 3.总结 事务是一种机制&#xff0c;一个操作序列。包含了一组数据库操作命令。

静态数码管——FPGA

文章目录 前言一、数码管1、数码管简介2、共阴极数码管or共阳极数码管3、共阴极与共阳极的真值表 二、系统设计1、模块框图2、RTL视图 三、源码1、seg_led_static模块2、time_count模块3、top_seg_led_static(顶层文件) 四、效果五、总结六、参考资料 前言 环境&#xff1a; 1、…

大学生用一周时间给麦当劳做了个App(微信小程序版)

背景 有个大学生粉丝最近私信联系我&#xff0c;说基于我之前开源的多语言项目做了个仿麦当劳的项目&#xff0c;虽然只是个样子货&#xff0c;但是收获颇多&#xff0c;希望把自己写的代码开源出来供大家一起学习进度。这个小伙伴确实是非常积极上进&#xff0c;很多大学生&a…

MySQL数据库(三)

前言 聚合查询、分组查询、联合查询是数据库知识中最重要的一部分&#xff0c;是将表的行与行之间进行运算。 目录 前言 一、聚合查询 &#xff08;一&#xff09;聚合函数 1、count 2、sum 3、avg 4、max 5、min 二、分组查询 &#xff08;一&#xff09;group by …

Docker架构

目录 Docker总架构图Docker ClientDocker DaemonDocker ServerDocker EngineJob Docker RegistryGraphDriverGraphDriverNetworkDriverExecDriver LibcontainerDocker Container Docker可以帮助用户在容器内部快速自动化部署应用&#xff0c;并利用Linux内核特性命名空间&#…

微软将推出更多Edge特有功能,与Chrome展开竞争

微软在 2018 年宣布将推出基于 Chromium 构建的 Edge 浏览器&#xff0c;并于 2020 年 1 月推出了新版 Edge。如今时隔三年&#xff0c;根据统计 Edge 全平台的市场占有率仅为 4.23%&#xff0c;如果只考虑桌面端的话&#xff0c;Edge 的市场占有率则是 10.98%&#xff0c;这两…

系统设计蓝图 / 备忘单

开发一个强大、可扩展和高效的系统可能会令人望而却步。然而&#xff0c;了解关键概念和组件可以使这个过程更可管理。在本博客文章中&#xff0c;我们将探讨系统设计的关键概念和组件&#xff0c;如DNS、负载均衡、API网关等&#xff0c;以及一个简明的备忘单&#xff0c;可以…

inux运维面试题(二)之系统管理类面试题

Linux运维面试题&#xff08;二&#xff09;之系统管理类面试题 1.权限优化1.1 简述Linux权限划分原则文件基本权限默认权限特殊权限sudo授权文件系统属性权限 解答 2.备份策略2.1需要备份的内容备份策略备份频率备份存储位置 2.2网站服务器每天产生的日志数量较大&#xff0c;…

LLM - 读取 Lora 模型进行文本生成

目录 一.引言 二.Lora 模型文本生成 1.模型读取 1.1 AutoModelForCausalLM.from_pretrained 1.2 PeftModel.from_pretrained 2.文本生成 2.1 Tokenizer 2.2 model.generate 3.输出实践 三.总结 一.引言 前面介绍了使用 Baichuan7B 从样本生成到 Lora 模型微调和存储…

磁盘擦写次数计算

1.让机器能有外网 2,安装工具 sudo apt-get install smartmontools 3,输入查询命令 sudo smartctl -x /dev/sda |egrep Device Model|User Capacity|Sector Size|173|Logical Sectors Written|Percentage Used Endurance Indicator 4,计算擦写次数 计算方法&#xff1a;25…

hadoop启动无法启动datanode或者namenode

首先进入hadoop安装目录下例如&#xff1a; 再进入dfs目录下&#xff0c;没有出现在hdfs-site.xml配置的data或者name cd data/current vim VERSION 打开VERSION文件复制&#xff1a;clusterID下的内容 将内容复制到name中的VERSION。再进行重启Hadoop