ExpressJS中res.sendFile遇客户端中止请求导致服务器崩溃
解决Express高并发中止请求时的ERR_HTTP_HEADERS_SENT问题
问题根源
当客户端中止请求(如JMeter中断连接)时,res.sendFile的回调仍会触发错误逻辑,此时响应头可能已发送或连接已断开,调用res.status(500).send会尝试重复设置响应头,触发ERR_HTTP_HEADERS_SENT错误。该错误未被正确捕获时会直接终止Node进程。开头的if (res.headersSent) return仅在接口启动时检查,无法覆盖请求中途中止的场景。
解决方案
1. 在sendFile回调中检查响应状态
在发送错误响应前,先确认响应是否仍可写入,避免重复操作:
app.get("/getFile", (req, res) => { try { res.sendFile( path.join(__dirname, "latest-data", "my-file.json"), (err) => { // 检查响应是否已结束或不可写入 if (res.headersSent || !res.writable) return; if (err) { res.status(500).send("Internal Server Error"); } } ); } catch (error) { console.error(error.stack); } });
2. 监听请求中止事件
捕获客户端中止请求的事件,提前终止后续响应逻辑:
app.get("/getFile", (req, res) => { let requestAborted = false; // 监听请求中止事件 req.on('abort', () => { requestAborted = true; }); try { res.sendFile( path.join(__dirname, "latest-data", "my-file.json"), (err) => { if (requestAborted || res.headersSent || !res.writable) return; if (err) { res.status(500).send("Internal Server Error"); } } ); } catch (error) { console.error(error.stack); } });
3. 全局捕获未处理异常(兜底方案)
添加全局异常监听,防止单个请求的错误导致整个服务器进程崩溃:
process.on('uncaughtException', (err) => { if (err.code === 'ERR_HTTP_HEADERS_SENT') { // 针对该错误做日志记录,无需终止进程 console.warn("Attempted to set headers after they were sent:", err.message); } else { // 其他严重错误仍需记录并处理 console.error("Uncaught exception:", err.stack); // 可选:根据实际情况决定是否重启进程 // process.exit(1); } }); process.on('unhandledRejection', (reason, promise) => { console.error("Unhandled Rejection at:", promise, "reason:", reason); });
额外优化点
- 去掉接口的
async关键字:res.sendFile不返回Promise,async无实际作用,反而可能误导错误捕获逻辑。 - 考虑使用
res.sendFile的options参数配置超时,避免长时间挂起的请求占用资源。
内容的提问来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

