hyperf 定时任务进程需手动配置 processstophandler 并轮询 context::get('process.stopping') 实现优雅停机,区分幂等型、状态迁移型、外部依赖型任务处理逻辑,并配合 k8s prestop 设置足够 terminationgraceperiodseconds。

Hyperf 定时任务进程(如 TimerProcess 或自定义的 CustomProcess)在收到 SIGTERM 时,若未主动感知停止信号并完成当前任务,容易导致定时逻辑中断、状态不一致或任务丢失。它不像 HTTP Worker 那样内置流量控制,必须靠开发者显式干预才能实现真正“优雅”收尾。
定时任务进程必须手动注册 ProcessStopHandler
Hyperf 默认只对 Worker 进程启用优雅停机(WorkerStopHandler),而定时任务这类自定义进程需单独配置信号处理逻辑:
- 在
config/autoload/signal.php的handlers数组中,显式添加Hyperf\Signal\Handler\ProcessStopHandler::class - 确保其优先级设为
PHP_INT_MIN,避免被其他 handler 覆盖 - 该 handler 会监听
SIGTERM/SIGINT,并设置Context::set('process.stopping', true)—— 这是你后续判断的唯一可靠依据
任务执行中需主动轮询 stopping 状态
定时任务通常运行在长循环或 while(true) 中,不能依赖外部中断。必须在关键位置检查是否应退出:
- 每次 tick 开始前,调用
Context::get('process.stopping', false)判断 - 若为
true,不再启动新任务;已开始的任务应允许跑完,但禁止进入下一轮循环 - 避免在 handler 回调里直接 exit() 或做耗时操作(如 DB 写入),只设标志 + 记日志
- 示例逻辑:
while (! Context::get('process.stopping', false)) {<br> doOneJob();<br> Co::sleep(1);<br>}
清理阶段要区分“可中断”与“不可中断”任务
不是所有任务都能随时中止。需按语义分类处理:
- 幂等型任务(如刷新缓存、同步配置):可立即终止,下次启动自动重试
- 状态迁移型任务(如订单状态推进、库存扣减):必须等待当前事务 commit 后再退出,否则数据错乱
-
外部依赖型任务(如调第三方 API、发邮件):建议加超时控制(
go(...)->withTimeout(5)),避免卡死阻塞停机流程
配合 Kubernetes preStop 留足缓冲时间
在 K8s 环境中,terminationGracePeriodSeconds 必须大于定时任务单次最长执行耗时:
- 若任务平均耗时 8s、最长可能 25s,则
terminationGracePeriodSeconds: 30是底线 - preStop 中不要重复发
kill -TERM,仅用于预留缓冲(如sleep 5),防止信号冲突 - 可在进程启动时记录 PID 和 start time,停机时打印已运行时长,用于后续容量评估











