为什么使用setInterval的Node.js脚本运行一段时间后会停止执行?
问题核心原因
你遇到的问题和PM2部署关系不大,主要是setInterval的使用逻辑有问题,加上异步任务没有做错误处理和资源管控,累计到阈值后触发了阻塞/资源耗尽:
- 第一,
setInterval不会等待异步任务执行完成就会推入下一次任务。你写的3个函数都是调用API、写数据库的IO密集异步任务,如果某次三个函数总执行时间超过2分钟,setInterval会直接把下一轮的三个函数塞进事件队列,多轮堆叠后Node进程的内存、socket连接、数据库连接都会被打满,后续的任务自然无法执行。 - 第二,你没有给异步函数加错误捕获和资源释放逻辑。虽然你不在意单次失败,但如果函数抛出未捕获的异常,或者API请求超时没有主动中断、数据库游标没有关闭,这些问题会累计,最终导致进程的可用资源耗尽,新的任务无法分配资源执行。
- 第三,EC2的默认资源限制比本地开发环境严格。比如默认的TCP连接数、文件句柄数、MongoDB连接池大小都更低,本地环境资源充足能扛住的泄漏问题,在EC2上会更快暴露。
修复方案
第一步:把setInterval换成递归的setTimeout,保证上一轮任务完全执行完再调度下一轮
示例代码:
// 封装调度函数,每次执行完所有任务后再调度下一次 const scheduleJob = async () => { try { // 如果你需要三个函数并行执行,用Promise.all,如果需要串行就依次await await Promise.all([function1(), function2(), function3()]) } catch (err) { // 统一捕获错误,避免未捕获异常影响进程 console.error('任务执行出错:', err) } finally { // 无论成功失败,2分钟后调度下一次 setTimeout(scheduleJob, 1000 * 120) } } app.listen(port, () => { // 启动第一次调度 scheduleJob() })
第二步:给单个函数加上超时控制和资源释放逻辑
- 给每个API请求加上超时时间,超时后主动中断请求,避免socket一直挂着占资源
- 给MongoDB的写操作加超时,用完的连接及时释放回连接池,不要保持无用的长连接
- 单个函数内部加上try/catch,避免单个函数的错误影响其他函数执行
第三步:PM2配置优化
- 给PM2的启动配置加上
--time参数,记录日志的时间戳,方便排查哪次任务出了问题 - 适当调高EC2实例的文件句柄数、TCP连接数限制,同时把MongoDB的连接池大小调整到合理范围,不要超过实例的承载上限
内容的提问来源于stack exchange,提问作者willmahon
相关产品推荐
相关产品推荐

