Node.js 后台任务队列:先保证可恢复,再追求吞吐

Node.js 后台任务队列:先保证可恢复,再追求吞吐
Node.js 后台任务队列先保证可恢复再追求吞吐全栈应用里经常需要后台任务发送邮件、生成报表、同步数据、调用模型、处理上传文件。很多团队一开始用setTimeout或简单数组就把任务跑起来了等到进程重启、任务失败、重复执行、用户催结果时才发现没有可恢复机制。后台任务队列的第一目标不是吞吐而是任务不会无声丢失。Node.js 做任务队列可以很轻量但必须把状态、重试、幂等和观测设计清楚。一、任务状态要落库stateDiagram-v2 [*] -- pending pending -- running running -- success running -- failed failed -- pending: retry running -- pending: timeout recovery只存在内存里的任务进程一重启就没了。即使用 Redis 或消息队列也建议在数据库里保存业务任务状态。队列负责调度数据库负责可追踪。这样用户刷新页面时能看到任务进度运维也能排查任务卡在哪里。状态字段至少包括 pending、running、success、failed。还要记录尝试次数、最后错误、锁定 worker、下次重试时间和创建时间。字段看起来多但它们是故障恢复的基础。二、执行必须幂等async function runJob(job) { if (await alreadyCompleted(job.id)) return; await doWork(job.payload); await markCompleted(job.id); }后台任务一定会遇到重复执行worker 超时后任务被重新捞起网络异常导致状态没写成功部署重启时不确定任务是否完成。如果任务不幂等重复执行可能发两封邮件、扣两次款、生成两份报告。幂等可以通过业务唯一键、状态检查、结果缓存和外部系统 idempotency key 实现。不要指望“队列只投递一次”工程上更现实的假设是至少一次投递然后让执行逻辑能承受重复。三、重试要有上限和退避retry: max_attempts: 5 backoff: type: exponential base_seconds: 30失败任务不能无限重试。供应商接口挂了、输入数据坏了、权限过期了无限重试只会制造更多噪音。重试策略要区分临时错误和永久错误并设置最大次数。超过次数后进入 dead letter 或人工处理队列。退避也很重要。立即重试可能撞上同一个故障窗口指数退避能减少系统压力。对于模型调用、第三方 API、邮件服务这类外部依赖退避和限流要一起考虑。四、监控要面向业务SELECT status, count(*) FROM jobs WHERE created_at now() - interval 1 hour GROUP BY status;任务队列的监控不只是 CPU 和内存。更重要的是 pending 积压、running 超时、失败率、重试次数、平均完成时间和最老任务等待时间。用户感受到的是任务是否完成不是 worker 是否健康。告警也要按业务区分。报表生成延迟 5 分钟可能可以接受支付回调处理延迟 5 分钟就很危险。队列系统要支持任务类型维度的指标否则一个低优先级任务堆积可能掩盖高优先级任务故障。五、总结Node.js 后台任务队列要先保证可恢复再追求吞吐。任务状态落库、执行幂等、有限重试、退避策略和业务监控是轻量队列也不能省的部分。吞吐可以后续优化任务丢失和重复副作用却很难补救。后台任务越重要越要先把恢复路径设计好。