如何确保基于Resque的ActiveJob后台任务始终正常运行?
解决Resque Worker崩溃与持续运行的保障方案
我之前折腾过好一阵子Resque+Capistrano的部署,太懂你这种担心worker挂掉、任务没人处理的焦虑了!先给你明确答案:Resque Worker确实会崩溃——不管是未捕获的任务异常、内存泄漏撑爆进程、系统资源不足,甚至是偶尔的网络波动都可能把worker搞挂。所以必须加一层保障机制,不然后台任务很容易“失联”。
下面是我实践过的几种标准解决方案,从基础到进阶都有:
一、用进程管理工具兜底(核心方案)
Resque本身没有自带进程守护和自动重启的能力,所以必须依赖外部工具监控worker进程,挂了就自动拉起来。最常用的有这两个:
1. Systemd(Linux系统首选)
现在大部分主流Linux发行版都用systemd,它自带的进程监控功能足够稳定,不用额外装软件。
你可以创建一个systemd模板服务文件(比如/etc/systemd/system/resque-worker@.service):
[Unit] Description=Resque Worker Instance %i After=network.target [Service] User=deploy # 换成你的部署用户 Group=deploy WorkingDirectory=/var/www/your_app/current # 你的应用当前目录 Environment="RAILS_ENV=production" # 启动命令,QUEUE=*可以换成你需要的队列,COUNT=1表示每个实例一个worker ExecStart=/bin/bash -lc 'bundle exec rake resque:work QUEUE=* COUNT=1' Restart=always # 不管进程怎么死的,都自动重启 RestartSec=5 # 崩溃后5秒重启 KillMode=process [Install] WantedBy=multi-user.target
配置好后,你可以启动多个worker实例(比如启动2个):
systemctl start resque-worker@1 resque-worker@2 systemctl enable resque-worker@1 resque-worker@2 # 设置开机自启
这样不管worker是崩溃还是被意外杀死,systemd都会自动帮你重启,完全不用手动干预。
2. Monit(轻量级监控+告警)
如果需要更精细化的监控(比如CPU/内存告警、邮件通知),Monit是个不错的选择。
先安装monit,然后在/etc/monit/conf.d/resque.conf里添加监控配置:
# 监控第一个worker进程 check process resque_worker_1 with pidfile /var/www/your_app/shared/tmp/pids/resque-1.pid start program = "/bin/bash -lc 'cd /var/www/your_app/current && bundle exec rake resque:work QUEUE=* COUNT=1 PIDFILE=/var/www/your_app/shared/tmp/pids/resque-1.pid'" as uid deploy and gid deploy stop program = "/bin/bash -lc 'cd /var/www/your_app/current && bundle exec rake resque:stop PIDFILE=/var/www/your_app/shared/tmp/pids/resque-1.pid'" if does not exist for 2 cycles then restart # 进程消失就重启 if cpu usage > 90% for 3 cycles then alert # CPU过高告警 if memory usage > 80% for 3 cycles then alert # 内存过高告警 # 可以复制上面的配置,添加更多worker实例 check process resque_worker_2 with pidfile /var/www/your_app/shared/tmp/pids/resque-2.pid # 内容和上面类似,改pid文件名即可
配置好后重启monit,它会定时检查worker状态,挂了就重启,还能给你发邮件告警。
二、Capistrano部署时的优化
用capistrano-resque部署时,要确保部署后worker能加载新代码,同时避免旧进程残留:
- 在你的
deploy.rb里配置重启命令(对应你用的进程管理工具):
这样部署完成后,Capistrano会自动重启所有worker,加载最新代码。# 如果用systemd set :resque_restart_cmd, 'systemctl restart resque-worker@*' # 如果用monit set :resque_restart_cmd, 'monit restart all resque_worker_*' - 不要用
COUNT=N一次性启动多个worker,最好每个worker单独管理(比如systemd的@实例或monit的多个进程配置),这样单个worker崩溃不会影响其他实例。
三、额外的防护细节
- 捕获任务异常:在ActiveJob任务里尽量捕获所有可能的异常,避免未处理的异常直接搞挂worker。比如:
class YourJob < ApplicationJob queue_as :default def perform(args) # 你的业务逻辑代码 rescue StandardError => e # 记录详细错误日志 Rails.logger.error "Job #{self.class.name} failed with args #{args}: #{e.message}\n#{e.backtrace.join("\n")}" # 可选:将任务重新入队,稍后重试 retry_job wait: 5.minutes, queue: :retry end - 定期重启worker:即使worker没崩溃,长期运行可能会有内存泄漏,建议每天定时重启一次(比如用cron配合systemd:
0 3 * * * systemctl restart resque-worker@*),避免内存耗尽。 - 监控队列长度:用Resque自带的
resque-web面板,或者自己写个简单的脚本监控队列长度。如果某个队列的任务数持续增长,说明worker可能挂了或者处理不过来,及时告警。
内容的提问来源于stack exchange,提问作者Honza
相关产品推荐
相关产品推荐

