使用 Supervisor 守护 RQ Worker:生产环境进程管理与配置实战
【免费下载链接】rqSimple job queues for Python项目地址: https://gitcode.com/gh_mirrors/rq/rq
Supervisor 是生产环境中管理 RQ Worker 这类长驻进程的经典工具,它能自动重启崩溃的进程,并用统一的面板集中查看构成产品的所有进程状态。本文以 RQ 仓库中的官方指南 docs/patterns/supervisor.md 为主体,结合rq worker命令的源码实现,讲解 supervisor 配置文件的每个关键字段、RQ 对终止信号的特殊要求,以及 virtualenv 与 Conda 环境下的完整部署方案。读完本文,你将能够为 RQ Worker 写出一份可直接上生产、可自动拉起、可优雅停机的 supervisor 配置。
为什么用 Supervisor 管理 RQ Worker
RQ Worker 是一个典型的"永远在跑"的进程:它从 Redis 队列中不断取出任务并执行,队列为空时就阻塞等待新任务。这样的进程一旦被误杀、崩溃或服务器重启,就需要有人把它重新拉起来——这正是 Supervisor 的用武之地:
- 自动重启:进程意外退出时自动拉起,保证任务消费能力不中断;
- 统一管理:通过
supervisorctl或 Web 面板查看/控制所有受管进程,产品由多个服务组成时尤其方便; - 与 RQ 天然契合:RQ 官方在 docs/docs/workers.md 中明确建议,生产环境应使用 Supervisor 或 systemd 这类进程管理器来运行 Worker。
RQ 与 Supervisor 的结合非常简单,核心就是一个[program:xxx]配置块。下面从最常用的配置说起。
基础配置:把 RQ Worker 交给 Supervisor
把下面这份配置写入 Supervisor 的配置目录(如/etc/supervisor/conf.d/rqworker.conf),这是官方文档给出的推荐设置:
[program:myworker] ; Point the command to the specific rq command you want to run. ; If you use virtualenv, be sure to point it to ; /path/to/virtualenv/bin/rq ; Also, you probably want to include a settings module to configure this ; worker. For more info on that, see docs/docs/workers.md command=/path/to/rq worker -c mysettings high default low ; process_num is required if you specify >1 numprocs process_name=%(program_name)s-%(process_num)s ; If you want to run more than one worker instance, increase this numprocs=1 ; This is the directory from which RQ is ran. Be sure to point this to the ; directory where your source code is importable from directory=/path/to ; RQ requires the TERM signal to perform a warm shutdown. If RQ does not die ; within 10 seconds, supervisor will forcefully kill it stopsignal=TERM ; These are up to you autostart=true autorestart=true这份配置虽短,但每一行都对应着 RQ 运行机制的一个关键点,逐一拆解如下。
command:启动命令的三种写法
command是 Supervisor 要执行的进程启动命令,写法取决于你的 Python 环境:
| 场景 | 写法 | 说明 |
|---|---|---|
| 系统级 Python | command=/path/to/rq worker -c mysettings high default low | 直接用 RQ 的可执行脚本 |
| virtualenv | command=/path/to/virtualenv/bin/rq worker ... | 务必指向虚拟环境内的rq可执行文件,而不是系统全局的 |
| Conda env | command=/opt/conda/envs/myenv/bin/rq worker ... | 指向 Conda 环境目录下的rq,详见下文 Conda 小节 |
命令中的high default low是队列名列表,Worker 按给定顺序监听这些队列,优先级从左到右递减:先消费完high队列的任务,再处理default,最后是low。
-c mysettings让 Worker 从mysettings模块读取配置(即mysettings.py)。从源码看,rq/cli/helpers.py 的read_config_file会导入该模块并读取其中所有大写命名的变量作为设置,因此你可以在配置模块中声明REDIS_URL、QUEUES、NAME、DICT_CONFIG等项(完整支持项见 docs/docs/workers.md 的 "Using a Config File" 一节)。注意 Supervisor 默认不会把当前目录加入PYTHONPATH,所以同时要用下面的directory指对工作目录。
process_name 与 numprocs:多 Worker 实例
process_name=%(program_name)s-%(process_num)s numprocs=1numprocs指定启动多少个进程实例。想横向扩展消费能力时,把它调大即可(例如numprocs=4表示同时跑 4 个 Worker)。- 一旦
numprocs > 1,process_name必须设置,否则 Supervisor 无法区分同名进程。%(program_name)s对应[program:myworker]的名字myworker,%(process_num)s是实例序号,最终进程名为myworker-0、myworker-1……
关于并发模型,需要澄清一点:单个 RQ Worker 内部是串行处理任务的(每个 Worker 同一时刻只执行一个 Job,任务在子进程"work horse"中运行)。要提高并发吞吐,正确姿势就是多开 Worker 进程——用numprocs放大,或使用 RQ 1.14.0 起的rq worker-pool命令(见 rq/cli/workers.py 的worker_pool实现)。两种方式在 Supervisor 下都可行。
directory:工作目录决定了 import 是否成功
directory=/path/toWorker 从这个目录启动,Python 才能把你的业务代码(任务函数所在模块)导入进来。务必指向你的源码可被 import 的目录,一般就是项目的根目录。这也是配合-c mysettings使用的前提之一——如果mysettings.py在项目根目录,而directory指错了地方,Worker 会直接因导入失败而退出。
stopsignal=TERM:RQ 优雅停机的前提
stopsignal=TERM这是整个配置里与 RQ 行为耦合最深的一行,也是最容易踩坑的地方。
RQ 的 Worker 在启动时会安装SIGINT与SIGTERM的信号处理器(见 rq/worker/base.py 的_install_signal_handlers)。收到一次SIGTERM时,Worker 执行的是暖关闭(warm shutdown):停止接收新任务,但会等当前正在执行的任务跑完,然后优雅地注销自身(源码见 rq/worker/base.py 的request_stop)。这一行为在测试中也有明确验证,例如 tests/test_worker.py 中的test_working_worker_warm_shutdown:向正在执行任务的 Worker 发送一次 SIGTERM,任务仍能正常完成,之后 Worker 才退出。
如果在暖关闭过程中再次收到SIGINT或SIGTERM,Worker 会转入冷关闭(cold shutdown):立即向子进程发送SIGKILL强杀当前任务,然后退出(见 rq/worker/base.py 的request_force_stop)。对应测试为test_working_worker_cold_shutdown:忙时发两次 SIGTERM,Worker 立即抛SystemExit,正在运行的任务被中断。
因此 Supervisor 这边必须stopsignal=TERM(而不是默认的TERM之外的信号),才能触发 RQ 的暖关闭。配合 Supervisor 默认的 10 秒stopwaitsecs:如果 Worker 在 10 秒内没有正常退出(比如当前任务执行了很久),Supervisor 会强制SIGKILL掉它——这相当于人工触发了一次冷关闭,属于兜底手段。如果生产任务单个执行时间较长,可以适当调大stopwaitsecs,给暖关闭留足时间。
autostart 与 autorestart
autostart=true autorestart=trueautostart=true:Supervisor 自身启动(如开机/supervisord启动)时自动拉起该程序;autorestart=true:进程异常退出时自动重启。
这两项是"守护"二字的落点,按需设置即可。
Conda 环境:为非 Python 依赖提供运行环境
当 RQ 任务需要非 Python 的依赖(例如 C 库、系统工具)时,可以借助 Conda 虚拟环境来承载这些依赖。官方文档给出的思路与 virtualenv 完全一致,只是路径换成 Conda 环境:
[program:myworker] ; Point the command to the specific rq command you want to run. ; For conda virtual environments, install RQ into your env. ; Also, you probably want to include a settings module to configure this ; worker. For more info on that, see docs/docs/workers.md environment=PATH='/opt/conda/envs/myenv/bin' command=/opt/conda/envs/myenv/bin/rq worker -c mysettings high default low ; process_num is required if you specify >1 numprocs process_name=%(program_name)s-%(process_num)s ; If you want to run more than one worker instance, increase this numprocs=1 ; This is the directory from which RQ is ran. Be sure to point this to the ; directory where your source code is importable from directory=/path/to ; RQ requires the TERM signal to perform a warm shutdown. If RQ does not die ; within 10 seconds, supervisor will forcefully kill it stopsignal=TERM ; These are up to you autostart=true autorestart=true与基础版相比,区别只在两处:
environment=PATH='/opt/conda/envs/myenv/bin':把 Conda 环境的bin目录注入PATH。这样 Worker 在 fork 出的子进程里执行任务时,能找到该环境内的可执行程序(比如任务调用的某个命令行工具)。如果任务还需要该环境里的动态库,可以进一步追加LD_LIBRARY_PATH之类的键值对,多个变量用逗号分隔。command指向环境内的rq:前提是先把 RQ 安装进这个 Conda 环境(如conda install -n myenv rq或pip install rq)。注意这里和 virtualenv 一样,指向的是环境内部的rq可执行文件。
其余字段(process_name、numprocs、directory、stopsignal=TERM、autostart、autorestart)的含义与基础版完全相同。
配套的 RQ 配置文件
Supervisor 里的-c mysettings需要一个真正的mysettings.py。官方推荐的配置项如下,可直接作为模板:
REDIS_URL = 'redis://localhost:6379/1' # You can also specify the Redis DB to use # REDIS_HOST = 'redis.example.com' # REDIS_PORT = 6380 # REDIS_DB = 3 # REDIS_PASSWORD = 'very secret' # Queues to listen on QUEUES = ['high', 'default', 'low'] # If you want custom worker name # NAME = 'worker-1024' # If you want to use a dictConfig for more complex/consistent logging DICT_CONFIG = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'standard': { 'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s' }, }, 'handlers': { 'default': { 'level': 'INFO', 'formatter': 'standard', 'class': 'logging.StreamHandler', 'stream': 'ext://sys.stderr', # Default is stderr }, }, 'loggers': { 'root': { # root logger 'handlers': ['default'], 'level': 'INFO', 'propagate': False }, } }要点说明:
REDIS_URL优先;也可以改用REDIS_HOST/REDIS_PORT/REDIS_DB/REDIS_PASSWORD组合。若设置了QUEUES,命令行上的队列名可以省略——源码 rq/cli/workers.py 中queues = queues or settings.get('QUEUES', ['default']),命令行参数优先于配置文件。NAME可指定自定义的 Worker 名,方便在rq info与监控面板中识别。DICT_CONFIG使用 Pythonlogging.config.dictConfig做复杂日志配置;RQ 也尊重应用先配置好的日志处理器,避免重复配置。- 从 rq/cli/helpers.py 的
get_redis_from_config可以看出,配置文件还支持REDIS_SSL、REDIS_SSL_CA_CERTS等 SSL 选项以及SENTINEL字典(Redis Sentinel 场景),有需要的可查阅源码后按需添加。
配置完成后:如何验证与运维
- 重载配置并启动:
supervisorctl reread然后supervisorctl update(或直接supervisorctl start myworker)。 - 查看状态:
supervisorctl status,应看到myworker处于RUNNING。 - 观察日志:
supervisorctl tail -f myworker,正常会看到类似*** Listening for work on high, default, low的输出。 - 验证优雅停机:
supervisorctl stop myworker,此时 Worker 若正在执行任务,会等任务完成后才退出(暖关闭);对正在跑的任务可以先手动确认其确实执行完毕。源码层面的依据可查看 tests/test_worker.py 中的WorkerShutdownTestCase,它覆盖了空闲暖关闭、忙时暖关闭与忙时冷关闭三条路径。 - 验证自动重启:
supervisorctl signal KILL myworker(模拟崩溃),Supervisor 应依据autorestart=true自动拉起新进程。
补充:另一个选择 systemd
如果你的发行版内置 systemd(多数现代 Linux 发行版),也可以参考 RQ 官方的另一种模式 docs/patterns/systemd.md:通过rqworker@.service模板单元文件配合systemctl start rqworker@1.service启动多实例,ExecStop=/bin/kill -s TERM $MAINPID同样遵循"SIGTERM 触发暖关闭"的原则。两种进程管理器任选其一即可,不要在同一个 Worker 上重复叠加。
小结
把 RQ Worker 交给 Supervisor 管理,本质上就是回答四个问题:用什么命令启动(指向正确环境的rq,队列按优先级排列,-c指定配置模块)、在哪个目录启动(directory指向源码可导入的目录)、想要几个实例(numprocs配合process_name)、停机时发什么信号(stopsignal=TERM触发暖关闭)。掌握这几条,再结合 Conda 环境注入与 RQ 配置文件,即可得到一套可靠、可观测、可优雅上下线的生产级 Worker 守护方案。
相关参考:
- 官方模式指南:docs/patterns/supervisor.md、docs/patterns/systemd.md
- Worker 使用详解:docs/docs/workers.md
- CLI 命令实现:rq/cli/workers.py、rq/cli/helpers.py
- 信号处理与关闭逻辑:rq/worker/base.py
- 关闭行为测试:tests/test_worker.py
【免费下载链接】rqSimple job queues for Python项目地址: https://gitcode.com/gh_mirrors/rq/rq
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考