Hyperf 3.0 定时任务需分层防重复:单机用 singleton = true 配 mutex(推荐 Redis 类型),集群必须设 onOneServer = true 并搭配 Redis 分布式锁,同时注意秒级调度需启用 enable_millisecond、锁过期时间设为任务耗时的2–3倍。

Hyperf 3.0 的定时任务默认基于协程调度,轻量高效,但多进程或多实例部署时容易出现重复执行——这不是 bug,而是设计使然:每个 worker 进程都会扫描并尝试触发任务。要真正规避并发重复,需分层处理:先防本机多进程冲突,再防集群多节点冲突。
单机多进程防重复:启用 singleton + mutex
Hyperf 默认允许多个 worker 同时执行同一任务。开启 singleton 可确保同一时刻本机只有一个实例运行该任务;但仅靠它还不够,因为进程间无状态同步,需配合锁机制:
- 在任务类中设
public array $singleton = true; - 同时配置
public array $mutex = ['type' => 'redis'];(推荐)或['type' => 'file'] - 确保 Redis 已正确配置且连接池可用;file 类型锁依赖本地文件系统,不适用于容器化或共享存储异常场景
集群多节点防重复:必须用 onOneServer + Redis 分布式锁
当服务部署在 Kubernetes 多 Pod、Docker 多容器或服务器集群时,singleton 完全失效。此时必须启用集群级互斥:
- 将
onOneServer设为 true(注意不是onOneServer的拼写变体,是准确字段名) - 搭配
mutex使用 Redis 类型,并确认mutexPool指向可用的 Redis 连接池 - 锁过期时间
mutexExpires建议设为任务预期最大执行时长的 2–3 倍(如任务通常耗时 8 秒,可设为 30 秒),避免误释放
规则与执行时机的隐性风险
即使加了锁,以下情况仍可能导致“看似重复”:
-
秒级任务未开启毫秒支持:若 rule 是
*/3 * * * * *(6段格式),但配置中未设'enable_millisecond' => true,框架会截断首段,降级为分钟级,造成调度偏差 -
任务启动后不立即执行:Hyperf 的 Crontab 调度器按“下一个匹配时间点”触发,比如服务在 14:03:22 启动,rule 是
* * * * *,首次执行会在 14:04:00,而非立刻运行 -
日志/调试输出被缓冲:协程环境下
echo或var_dump可能延迟刷新,误判为多次执行;建议统一用$this->logger->info()并确保日志驱动实时写入
验证是否真正生效
不要只看日志频率,要抓关键证据:
- 启动时检查控制台是否有
[INFO] Crontab manager started with X tasks - 在
execute()中打印当前进程 ID:getmypid(),对比多次执行的 PID 是否相同(singleton 生效)或不同但结果一致(onOneServer 生效) - 手动在 Redis 中查锁 key:
task:{任务名}:lock,确认其存在、value 唯一、TTL 正常递减


















