Jest调用AWS SQS sendMessage时出现打开句柄无法退出问题
报错产生原因
这个问题出在AWS SDK v2的默认连接机制上:
- SDK默认开启HTTP长连接(keepAlive),会为每个服务实例维护独立的TCP连接池
- 你当前的代码在每次调用
sendToSQS时都会新建一个AWS.SQS()实例,sendMessage请求执行完成后,实例持有的底层连接不会主动关闭,对应的事件循环句柄会一直保持活跃 - Jest在所有测试用例执行完毕后,检测到仍有未释放的活跃句柄,就会抛出该提示,阻止进程正常退出。
解决方案(无需mock、无需配置--forceExit)
方案1:复用SQS客户端实例 + 测试收尾阶段主动销毁连接
这是最推荐的做法,既不影响性能,也能彻底释放句柄:
- 不要在方法内部重复创建SQS实例,将实例初始化逻辑移到类构造函数中全局复用:
// 类构造函数 constructor(awsConfig, queueUrl) { AWS.config.update({...awsConfig}); // 全局复用单个SQS实例 this.sqsClient = new AWS.SQS(); this.queueUrl = queueUrl; } public async sendToSQS(data){ return new Promise((resolve, reject) => { const params = { MessageBody: JSON.stringify(data), QueueUrl: this.queueUrl, }; this.sqsClient.sendMessage(params, (error, res) => { if (error) { return reject(error); } return resolve(res.MessageId); }); }); }
- 在测试文件的全局收尾钩子中,主动销毁AWS SDK持有的连接池,释放所有句柄:
afterAll(() => { // 销毁SQS实例的底层HTTP连接 sqsFile.sqsClient.config.httpOptions.agent.destroy(); });
方案2:关闭长连接配置,让请求完成后自动释放连接
如果不想调整实例初始化的结构,可以在创建SQS实例时关闭keepAlive配置,请求结束后TCP连接会自动断开,不会残留句柄:
public async sendToSQS(data){ return new Promise((resolve, reject) => { AWS.config.update({...this.awsConfig}); // 创建实例时关闭长连接 const sqs = new AWS.SQS({ httpOptions: { keepAlive: false } }); const params = { MessageBody: JSON.stringify(data), QueueUrl: queueUrl, }; sqs.sendMessage(params, (error, res) => { if (error) { return reject(error); } return resolve(res.MessageId); }); }); }
注意:这个方案会让每次SQS请求都重新建立TCP连接,生产环境下会有额外的性能开销,更适合测试场景使用。
内容的提问来源于stack exchange,提问作者Mukesh Kashyap
相关产品推荐
相关产品推荐

