Hyperf 高频定时任务需弃用 @Crontab + crontab-dispatcher,改用 beforeStart 启动单协程秒级循环,配合 RateLimit 限流、Redis 分布式锁及系统资源调优防 OOM。

Hyperf 每秒高频执行定时任务时,不能依赖默认的 @Crontab + crontab-dispatcher 进程模型——它不参与 Swoole 生命周期,每秒 new 实例且不回收,几天内就会 OOM。真正可行的限流方案,是绕过 dispatcher,用可控协程 + 资源约束 + 分布式协调三者结合。
用 beforeStart 启动单协程秒级循环
删掉所有 @Crontab 注解,在 ServerProvider::beforeStart() 中启动一个长期运行的协程,每秒执行一次轻量逻辑:
- 只创建一次协程,避免进程/实例爆炸
- 循环内必须加
ServerManager::isRunning()判断,防止服务停止后协程残留 - 业务逻辑要轻:避免在循环里 new 大对象、复用 DB 单例或持有静态缓存;需 DB 操作时,用
make()拿新实例 - 加 try-catch 包裹 execute 体,防止异常中断整个循环
对高频动作做本地速率控制
即使协程每秒跑一次,里面的具体操作(如发消息、写缓存、查 Redis)仍可能被压垮。这时要用 Hyperf\RateLimit 组件做细粒度限流:
- 在关键方法上加
@RateLimit(key="secondly:cache_refresh", maxAttempts=10, timeout=1),限制每秒最多 10 次执行 - key 建议带业务标识(如
"secondly:order_sync"),避免不同任务互相干扰 - 配合自定义
LimitInterface实现动态 key,比如按 worker ID 或机器 IP 区分,防止单点打满
多节点部署时用 Redis 分布式锁兜底
单协程解决不了集群多实例重复执行问题。哪怕每个节点只起一个协程,5 台机器仍会每秒各跑 1 次——总量翻 5 倍。必须引入跨节点协调:
- 在秒级循环里,每次执行前先尝试获取 Redis 锁:
$lock = $this->redis->set($key, '1', ['NX', 'EX' => 1]) - 只有拿到锁的节点才执行业务,其余直接跳过
- 锁过期时间严格设为 ≤1 秒(如 0.8s),避免某节点卡住导致后续全部阻塞
- 不依赖
mutex配置,因其底层仍走 dispatcher,而 dispatcher 正是泄漏源头
系统层资源必须同步放开
每秒新建协程、发连接、读写 Redis,若系统限制太紧,会出现静默失败:
-
LimitNOFILE=65536:65536必须写进 systemd unit 文件(/etc/systemd/system/hyperf.service),/etc/security/limits.conf对 Swoole 进程无效 - 检查
ulimit -n输出是否真为 65536,不是仅配置未生效 - 协程数上限(
swoole.max_coroutine)建议设为 10000+,避免秒级任务挤占 HTTP 请求协程资源


















