使用Express流式返回查询结果时出现Knex数据库连接获取超时问题
问题根本原因
你的代码只捕获了stream创建阶段的同步异常,没有处理stream运行过程中的异步错误,导致数据库连接泄漏,最终占满连接池触发超时,具体问题如下:
- 外层的
try/catch只能捕获db(...).stream()调用时立刻抛出的同步错误,stream在后续数据传输过程中出现的所有异步错误(比如客户端中途断开请求、网络波动中断传输、JSON序列化报错)都无法被捕获,这时候数据库连接不会被Knex主动回收,持续占用连接池配额。 - 原生的
pipe方法不会在下游stream出错/中断时自动销毁上游的数据库stream,哪怕响应提前终止,数据库侧的stream还会保持挂起状态,对应的连接一直不会释放。 - 并发请求多的时候,泄漏的连接慢慢占满整个连接池,新的请求拿不到空闲连接,就会随机抛出你遇到的连接获取超时报错。
修复方案
用Node.js内置的stream.pipeline代替原生pipe,它会自动在任意环节出错时销毁所有上下游stream,释放资源,同时补充完整的错误和中断场景处理,示例代码如下:
const { pipeline } = require('stream') const JSONStream = require('JSONStream') app.get('/events', (req, res, next) => { try { const stream = db('events') .select('*') .where({ id_user: 'foo' }) .stream() pipeline( stream, JSONStream.stringify(), res, (err) => { if (err) { stream.destroy(err) next(err) } } ) req.on('close', () => { if (!stream.destroyed) { stream.destroy() } }) } catch (err) { next(err) } })
内容的提问来源于stack exchange,提问作者nicholaswmin
相关产品推荐
相关产品推荐

