IIS集成池模式下Node.js+Angular的SSE无法正常工作
IIS集成应用池下SSE事件延迟的问题与修复建议
问题概述
在IIS集成应用池模式下,Server-Sent Events(SSE)事件出现延迟:前端需等待后端整个调用完成后,才会一次性收到从0%到100%的所有进度数据,但本地用ng serve运行时完全正常。已尝试禁用text/event-stream的动态压缩,但无效果。
技术栈:Node.js后端生成SSE事件,Angular前端接收。已验证请求头设置正确,排除了经典模式的压缩问题。
相关代码
Node.js后端代码
app.get(Config.rootAPIPath + '/progress', (req, res) => { // Set headers for Server-Sent Events res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); // Listen to progress events from business layer const progressListener = (data) => { res.write(`data: ${JSON.stringify(data)} `); if (data.processed == 100) { res.write(`event: complete\ndata: {"processed": 100}\n\n`); res.end(); } }; Stream.on('progress', progressListener); });
Angular组件代码
listenToServer() { this.eventSubscription = this.sseService.getServerSentEvents(`${Config.serverSideEventURL}progress`) .subscribe({ next: (event: any) => { this.zone.run(() => { console.log('Event received:', event); this.progress = JSON.parse(event.data); let counter = this.progress.processed; if (isNullOrUndefined(counter)) this.counter = 0; else this.counter = +counter; this.appRef.tick(); }); }, complete: () => { console.log('SSE connection completed'); this.isImporting = false; }, error: (error) => { console.error('SSE error:', error); this.isImporting = false; } }); }
Angular SSE服务代码
export class ServerSideEventService { constructor(private ngZone: NgZone) { } getServerSentEvents(url: string): Observable<any> { return new Observable(observer => { const eventSource = new EventSource(url); eventSource.onmessage = (event) => { this.ngZone.run(() => { observer.next(event); }); }; eventSource.addEventListener('complete', (event) => { this.ngZone.run(() => { observer.complete(); }); }); eventSource.onerror = (error) => { this.ngZone.run(() => { observer.error(error); }); }; return () => eventSource.close(); }); } }
修复建议
1. 强制Node.js响应立即推送
IIS可能会缓冲响应直到达到一定大小才发送,Node.js的res.write()默认不会立即将数据推送到客户端。每次调用res.write()后添加res.flush()(Express框架支持),确保数据即时发送:
const progressListener = (data) => { res.write(`data: ${JSON.stringify(data)} `); res.flush(); // 强制推送数据到客户端 if (data.processed == 100) { res.write(`event: complete\ndata: {"processed": 100}\n\n`); res.flush(); res.end(); } };
如果用原生Node.js HTTP模块,没有flush()方法,可在设置响应头后添加res.socket.setNoDelay(true)禁用Nagle算法,避免小数据包合并:
res.socket.setNoDelay(true);
2. 配置IIS禁用响应缓冲
即使是集成模式,IIS仍可能对长连接响应缓冲。在web.config中添加以下配置,针对SSE路径禁用缓冲:
<system.webServer> <httpProtocol> <customHeaders> <add name="X-Accel-Buffering" value="no" /> <!-- 告诉代理不要缓冲 --> </customHeaders> </httpProtocol> <serverRuntime appConcurrentRequestLimit="1000" /> </system.webServer>
3. 确认动态压缩排除配置
确保text/event-stream类型的动态压缩已完全禁用,在web.config中明确配置:
<system.webServer> <urlCompression doDynamicCompression="true"> <dynamicTypes> <add mimeType="text/event-stream" enabled="false" /> </dynamicTypes> </urlCompression> </system.webServer>
4. 检查反向代理/防火墙设置
如果服务器前部署了ARR、防火墙等代理设备,它们可能会缓冲长连接响应。需在这些设备中添加规则,禁用对text/event-stream类型的缓冲,或允许流式传输。
内容的提问来源于stack exchange,提问作者partyDj
相关产品推荐
相关产品推荐

