这个坑值得记一笔。Node 20 上用 Express 4.19 写了个流式接口,res.write 写完都调了 flush,本地 3000 端口用 curl 能看到逐条输出,套上 compression 中间件后 curl 只收到一个整包,前端 EventSource 一直停在连接中。
刚开始盯着「响应头在第一次 write 之后才设置,中间件已经决定压缩」,折腾了半个多小时,排掉了。
排查方向其实有限,先把可能性排一排。compression 中间件对 text/event-stream 也会做 gzip 缓冲;调了 res.flush 但没引入 compressing 提供的 flush 方法;用了 res.end 一次性返回,连接根本没保持住。
先看响应头有没有 Content-Encoding
curl -sI http://localhost:3000/stream | grep -i encoding
然后把事件流路径排除在压缩之外
app.use(compression({ filter: (req, res) => !String(res.getHeader('Content-Type')).includes('event-stream') }));
接着设头再写第一个事件
res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.write('data: hi\n\n');
末了用 curl 逐行验证输出节奏
curl -N http://localhost:3000/stream
隔了一天再看,现象没变,说明不是偶发。
那几天正好在做别的变更,先把时间线对齐才敢动手。
改完先别撤,确认这几项。响应头里不再有 Content-Encoding,curl 每秒收到一行 data,前端的 onmessage 连续触发。
补一句。压缩中间件按 Content-Type 判断,但流式响应头常常设置得偏晚,显式 filter 比调整中间件顺序更稳。这类问题最好一次改到位,反复小改反而容易把原因搅混。
