使用 Supervisor 守护 RQ Worker生产环境进程管理与配置实战【免费下载链接】rqSimple job queues for Python项目地址: https://gitcode.com/gh_mirrors/rq/rqSupervisor 是生产环境中管理 RQ Worker 这类长驻进程的经典工具它能自动重启崩溃的进程并用统一的面板集中查看构成产品的所有进程状态。本文以 RQ 仓库中的官方指南 docs/patterns/supervisor.md 为主体结合rq worker命令的源码实现讲解 supervisor 配置文件的每个关键字段、RQ 对终止信号的特殊要求以及 virtualenv 与 Conda 环境下的完整部署方案。读完本文你将能够为 RQ Worker 写出一份可直接上生产、可自动拉起、可优雅停机的 supervisor 配置。为什么用 Supervisor 管理 RQ WorkerRQ 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 numprocs1 ; 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 stopsignalTERM ; These are up to you autostarttrue autorestarttrue这份配置虽短但每一行都对应着 RQ 运行机制的一个关键点逐一拆解如下。command启动命令的三种写法command是 Supervisor 要执行的进程启动命令写法取决于你的 Python 环境场景写法说明系统级 Pythoncommand/path/to/rq worker -c mysettings high default low直接用 RQ 的可执行脚本virtualenvcommand/path/to/virtualenv/bin/rq worker ...务必指向虚拟环境内的rq可执行文件而不是系统全局的Conda envcommand/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 numprocs1numprocs指定启动多少个进程实例。想横向扩展消费能力时把它调大即可例如numprocs4表示同时跑 4 个 Worker。一旦numprocs 1process_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 会直接因导入失败而退出。stopsignalTERMRQ 优雅停机的前提stopsignalTERM这是整个配置里与 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或SIGTERMWorker 会转入冷关闭cold shutdown立即向子进程发送SIGKILL强杀当前任务然后退出见 rq/worker/base.py 的request_force_stop。对应测试为test_working_worker_cold_shutdown忙时发两次 SIGTERMWorker 立即抛SystemExit正在运行的任务被中断。因此 Supervisor 这边必须stopsignalTERM而不是默认的TERM之外的信号才能触发 RQ 的暖关闭。配合 Supervisor 默认的 10 秒stopwaitsecs如果 Worker 在 10 秒内没有正常退出比如当前任务执行了很久Supervisor 会强制SIGKILL掉它——这相当于人工触发了一次冷关闭属于兜底手段。如果生产任务单个执行时间较长可以适当调大stopwaitsecs给暖关闭留足时间。autostart 与 autorestartautostarttrue autorestarttrueautostarttrueSupervisor 自身启动如开机/supervisord启动时自动拉起该程序autorestarttrue进程异常退出时自动重启。这两项是守护二字的落点按需设置即可。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 environmentPATH/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 numprocs1 ; 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 stopsignalTERM ; These are up to you autostarttrue autorestarttrue与基础版相比区别只在两处environmentPATH/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、stopsignalTERM、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 应依据autorestarttrue自动拉起新进程。补充另一个选择 systemd如果你的发行版内置 systemd多数现代 Linux 发行版也可以参考 RQ 官方的另一种模式 docs/patterns/systemd.md通过rqworker.service模板单元文件配合systemctl start rqworker1.service启动多实例ExecStop/bin/kill -s TERM $MAINPID同样遵循SIGTERM 触发暖关闭的原则。两种进程管理器任选其一即可不要在同一个 Worker 上重复叠加。小结把 RQ Worker 交给 Supervisor 管理本质上就是回答四个问题用什么命令启动指向正确环境的rq队列按优先级排列-c指定配置模块、在哪个目录启动directory指向源码可导入的目录、想要几个实例numprocs配合process_name、停机时发什么信号stopsignalTERM触发暖关闭。掌握这几条再结合 Conda 环境注入与 RQ 配置文件即可得到一套可靠、可观测、可优雅上下线的生产级 Worker 守护方案。相关参考官方模式指南docs/patterns/supervisor.md、docs/patterns/systemd.mdWorker 使用详解docs/docs/workers.mdCLI 命令实现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),仅供参考
