Hyperf中协程与多进程定时任务分工协作:协程负责轻量高频IO调度分发,多进程(如Task Worker)承载耗时、CPU密集或需资源独占的任务执行,确保主协程不被阻塞。

Hyperf 中协程与多进程定时任务不是互斥关系,而是分工协作:协程负责轻量、高频、IO 密集型的调度与分发;多进程(如 Task Worker、CrontabDispatcherProcess)负责隔离耗时、CPU 密集或需资源独占的任务。混用的关键不在“能否一起用”,而在于“谁该在哪一层执行”。
协程层:负责 crontab 任务的注册、解析与触发分发
Hyperf 的 @Crontab 注解或配置式任务,全部由协程驱动:
- 框架启动时,
CrontabDispatcherProcess进程内运行一个协程定时器(Co::tick),每秒扫描一次所有已注册任务 - 协程根据 cron 表达式计算下次执行时间,匹配成功后,不直接执行业务逻辑,而是决定投递方式
- 默认情况下,它会把任务投递给
Task Worker进程(若启用了 task 功能),或在当前协程中同步执行(仅限极轻量逻辑,如日志打点)
多进程层:承载实际耗时任务执行,避免阻塞主协程
真正需要隔离执行的定时任务,应交由 Task Worker 进程处理:
- 在
@Crontab回调中,不要写数据库大批量更新、文件压缩、模型推理等操作;而是调用$this->container->get(TaskExecutor::class)->execute(...) - 确保
TaskExecutor类的handle()方法被定义在app/Task下,并通过task_enable_coroutine => true启用协程支持(前提是内部使用协程 MySQL/Redis 客户端) - 配置
task_worker_num在worker_num × 0.5 ~ worker_num × 1区间,例如worker_num = 6时设为4,防止 IPC 队列积压
明确边界:什么该用协程?什么必须进多进程?
判断依据是执行特征,而非任务类型名称:
- 适合协程内执行:记录执行日志、更新内存缓存(如 Swoole\Table)、发送轻量通知(如 Redis Pub/Sub)、校验规则并快速返回
- 必须进 Task Worker:涉及阻塞 IO(如未设超时的 cURL 同步请求)、CPU 密集计算(如 Base64 编码 10MB 图片)、长时间数据库事务、调用外部 CLI 工具(如 ffmpeg)
-
严禁混用场景:在
@Crontab方法里调sleep(3)、usleep(500000)或file_get_contents('http://slow-api')—— 这会让整个协程调度器卡住,影响所有定时任务和 HTTP 请求
动态任务 + 多进程安全的补充实践
当从数据库加载动态定时任务时,仍需保证投递安全:
- 每个动态任务解析出 cron 规则后,统一包装成
Crontab实例,但setCallback必须指向一个“调度转发器”,例如[TaskDispatcher::class, 'dispatch'] -
TaskDispatcher::dispatch()内部不做业务,只做参数序列化 + 投递到 Task Worker,由真正的处理器类(如ReportGenerator)在独立进程中完成生成报表逻辑 - 若部署多个 Hyperf 实例,启用
distributed_lock配置项,让 Crontab 组件自动基于 Redis 实现分布式锁,避免同一任务被多个节点重复触发











