Node.js可写流writable stream挂起:大量写入后res.end()不执行请求挂起
问题原因
1. 数据库连接池耗尽
pg-promise默认使用的连接池最大连接数为10,你代码中的双层循环单次请求要执行501 * 501 = 251001次独立SQL查询,当查询量超过连接池处理能力后,后续的await db.any()会进入无限等待队列,循环无法执行完毕,自然到不了res.end()的逻辑。
写入次数少的时候查询量很小,连接池足够支撑,循环可以快速走完,所以响应能正常结束。
2. HTTP响应背压未处理
res.write()方法会返回布尔值,当返回false时代表内核发送缓冲区已满,此时如果继续调用res.write(),写入的内容会全部缓存在Node.js进程内存中,随着内存占用持续升高,会导致进程响应变慢甚至卡住,进一步阻塞后续逻辑执行。
3. cork/uncork使用不合理
你每次调用res.write()前单独调用res.cork(),再通过process.nextTick()执行uncork(),这种写法相当于每次都强制缓冲单次写入的内容,反而降低了写入效率,加剧了内存堆积的风险。
解决方案
- 优化数据库查询逻辑:不要在循环中执行单条查询,改用IN条件批量查询所需数据,大幅减少查询次数,从根源避免连接池耗尽。
- 合理调整连接池参数:根据业务需要和PostgreSQL的最大连接限制,调整pg-promise的连接池大小,同时限制单次请求的查询并发数。
- 处理HTTP响应背压:当
res.write()返回false时,暂停循环和数据库查询,监听drain事件触发后再恢复执行,避免内存无限堆积。 - 调整cork/uncork使用逻辑:不需要每次写入都单独加cork,对于连续的批量写入操作,可以统一cork后写入所有内容,再统一uncork提升写入效率。
内容的提问来源于stack exchange,提问作者disgustingtom
相关产品推荐
相关产品推荐

