NestJS SSE:interval与fromEvent的客户端视角差异探究
为什么NestJS中两种SSE实现的多客户端行为差异?
核心原因在于Observable的创建逻辑、订阅上下文以及事件源的共享性完全不同,具体拆解如下:
1. interval实现:每个客户端拥有独立的私有数据流
当客户端连接/sse端点时,Nest会为每个连接单独调用sse()方法,返回一个全新的interval Observable:
interval属于冷Observable:每个订阅(即每个客户端连接)都会启动一个独立的定时器,维护自己的执行序列。也就是说,客户端A和客户端B的定时器是完全分开的,各自按5秒间隔触发。- 代码里的
this.c++每次在map中执行时,会基于控制器实例的共享变量递增,但因为两个客户端的定时器触发时机不同,最终收到的hello: world - X内容也会不一样(比如A先收到world-1,B连接后可能收到world-2,后续各自的计数也是独立推进的)。 - 本质是:每个客户端的Observable实例是独立的,数据流互不干扰。
2. fromEvent + EventEmitter实现:所有客户端共享全局事件流
/approvals端点的实现依赖Nest的单例EventEmitter:
- Nest默认将
EventEmitter注册为单例,所以整个应用中所有控制器注入的是同一个事件发射器实例。 fromEvent创建的Observable,本质是监听这个全局单例的NEW_APPROVAL_EVENT_NAME事件。不管多少客户端连接,每个客户端的订阅都会在同一个EventEmitter上注册监听器。- 当
NEW_APPROVAL_EVENT_NAME事件被触发时,EventEmitter会把同一份ApprovalDTO数据推送给所有注册的监听器,因此所有客户端都会收到完全相同的消息。
补充:如何让interval实现也共享数据流?
如果想让interval的SSE端点也让所有客户端收到相同消息,可以用share()操作符将冷Observable转为热Observable,让多个订阅共享同一个数据流:
@Sse('sse-shared') sseShared(): Observable<MessageEvent> { return interval(5000) .pipe( map((_) => ({ data: { hello: `world - ${this.c++}` }} as MessageEvent)), share() // 让所有订阅共享同一个定时器数据流 ); }
这样所有客户端会同步收到相同的计数消息。
内容的提问来源于stack exchange,提问作者Johnny Goodman
相关产品推荐
相关产品推荐

