生产环境下NestJS/Angular的SSE连接异常问题排查
我开发了一个基于NestJS/Angular的应用,通过Server Sent Events(SSE)向前端推送数据。本地运行时一切正常,但部署到生产环境后,前端用EventSource连接SSE的请求一直处于pending状态。我已经检查了所有配置和CORS设置,添加了必要的请求头,也尝试过将前后端部署在不同域名并配置CORS,甚至用Postman这类REST客户端测试,问题依然存在。
相关代码如下:
后端代码(event.controller.ts)
@Controller('event') export class EventController { // [...] 构造函数初始化部分 @Sse('sse/:id') @Header('Cache-Control', 'no-cache') @Header('Content-Type', 'text/event-stream') @Header('Access-Control-Allow-Origin', '*') @Header('Connection', 'keep alive') @Header('X-Accel-Buffering', 'no') sse( @Param('id') id: string, @Req() req: Request ): Observable<MessageEvent> { return this.service.subscribeToTopicUpdate(id) } }
前端代码(sse.service.ts)
getEventSourceFrom(eventId: string): EventSource { return new EventSource(`${environment.baseUrl}/event/sse/${eventId}`); }
网络控制台中请求始终处于pending状态,相关截图显示:
- 网络请求列表中该请求状态为pending
- 请求头包含预期的相关字段
1. 检查生产环境反向代理/负载均衡的缓冲配置
很多生产环境用的反向代理(比如Nginx)默认会缓冲响应,就算你在后端加了X-Accel-Buffering: no,也得在代理层明确禁用缓冲才能让SSE流实时传输。举个Nginx的配置例子:
location /event/sse { proxy_pass http://your-backend-service; proxy_set_header Connection ''; proxy_http_version 1.1; chunked_transfer_encoding off; proxy_buffering off; proxy_cache off; }
要是用的是Apache、Cloudflare这类其他代理,也得对应调整配置禁用响应缓冲。
2. 确保后端Observable发送初始消息
SSE连接建立后,后端必须先发送至少一条消息(哪怕是空内容),不然前端会一直处于pending状态。检查subscribeToTopicUpdate方法,看是不是订阅后立刻发了一条初始的MessageEvent,比如:
// 在service的subscribeToTopicUpdate方法里调整 return merge( // 发初始消息触发连接完成 of({ data: 'connected' } as MessageEvent), // 后续的更新消息流 this.topicUpdates$.pipe(map(data => ({ data }) as MessageEvent)) );
3. 调整生产环境的长连接超时设置
不少云服务或服务器防火墙会限制长连接的超时时间,比如AWS ALB、阿里云SLB这类负载均衡,得把长连接超时时间调到合适的值(比如5分钟以上),避免连接被提前断开。
4. 给前端EventSource加错误监听排查问题
你当前的前端代码没加错误监听,可能连接出问题了但没被捕获。加个监听看看具体错误:
const eventSource = new EventSource(`${environment.baseUrl}/event/sse/${eventId}`); eventSource.onerror = (error) => { console.error('SSE连接错误:', error); eventSource.close(); }; eventSource.onopen = () => { console.log('SSE连接已建立'); };
5. 验证NestJS的SSE推送逻辑是否正常
先确认subscribeToTopicUpdate返回的Observable是持续推送的,没有提前完成。如果Observable提前结束,SSE连接会被关闭,也可能表现为pending状态。可以先临时替换成一个简单的定时推送测试:
// 临时替换service的返回值,验证基础SSE功能 return interval(1000).pipe( map(count => ({ data: `update ${count}` } as MessageEvent)) );
内容的提问来源于stack exchange,提问作者akana

