task_worker_num合理范围是worker_num×0.5至worker_num×1,如worker_num=8则设4~8;超worker_num×2需警惕ipc积压与超时丢任务,须监控task_wait_queue_len及/dev/shm空间。

task_worker_num设多少才算合理
不能只看CPU核数或拍脑袋填个“8”或“16”。task_worker_num本质是为耗时任务预留的独立进程槽位,它和worker_num不是倍数关系,而是协同关系——Task Worker越多,IPC通信开销越大,积压任务反而更容易超时丢弃。
真实配比建议:task_worker_num取值在 worker_num * 0.5 到 worker_num * 1 之间较稳。比如worker_num => 8,那就设 task_worker_num => 4~8;若业务里大量跑模型推理或大文件转码,可顶格到8,但必须同步监控task_wait_queue_len。
- 超过
worker_num * 2就得警惕:IPC队列延迟上升、tasking_num长期高于task_worker_num,说明任务进得多、干得慢 - 单机
task_worker_num > 16时,务必确认/dev/shm可写且空间充足(task_tmpdir默认走这里),否则序列化参数失败会静默丢任务 - IO密集型任务(如批量发邮件)可略宽松;CPU密集型(如图像缩放)建议保守,宁可让任务排队,也别让Task Worker自己卡死
为什么开了task_enable_coroutine还是不生效
task_enable_coroutine => true只是开关,不是魔法。它只在Task Worker进程内启用协程调度器,但前提是:你写的任务方法里真用了协程API,且没混入阻塞调用。
常见失效点:
- 在
handle()里调了sleep()、usleep()或未加超时的curl_exec(),协程立刻退化成同步阻塞 - 用了非协程驱动的DB类(比如PDO直连MySQL),哪怕开了
task_enable_coroutine也没用 - 传递了不可序列化的对象(如
Closure、资源句柄、mysqli实例),任务根本投递不出去,日志里只有serialize(): __sleep should return an array
验证方式:在任务里加Co::sleep(0.01),再用strace -p $(pgrep -f 'task_worker') -e trace=epoll_wait看是否真挂起,而不是忙等。
投递任务时execute()卡住,怎么定位
TaskExecutor::execute()默认同步等待结果,卡住 ≠ 任务没执行,更可能是:任务已进队列但迟迟不被消费,或消费后返回失败却没抛异常。
排查路径:
- 查
swoole_server->stats()里的tasking_num:持续 > 0 说明任务正在跑;若长期为0但task_wait_queue_len飙升,就是Task Worker全忙/挂了 - 看日志有没有
task worker exit with code=0, signal=11——这是Segmentation Fault,大概率是C扩展冲突或内存越界 - 检查
task_max_request是否太小(如设成100),导致Task Worker刚干完一个任务就被强制重启,新任务只能排队 - 别忽略
timeout注解:#[Task(timeout: 5)]比硬编码set_time_limit(5)更可靠,后者在协程里无效
定时任务扔进Task Worker后为啥时间不准
Hyperf的@Crontab(process="task")只是把触发逻辑交出去,但任务体仍在Task Worker里执行——而Task Worker不继承Worker的协程上下文,go、defer、context全失效,Co::sleep()也会退化。
典型表现:本该每分钟跑一次的任务,实际间隔飘到1分20秒以上,甚至漏跑。
- 根本原因:CrontabDispatcherProcess本身是个长周期协程进程,一旦某个
@Crontab方法里写了阻塞操作(比如同步Redis写日志),整个调度tick就被拖慢 - 正确做法:所有耗时逻辑必须剥离出Crontab方法体,仅用
$this->container->get(TaskExecutor::class)->execute()投递;Crontab方法体里只做参数组装和投递动作 - 额外注意:Task Worker无时区上下文,
date('Y-m-d H:i:s')可能用的是UTC,得显式date_default_timezone_set('Asia/Shanghai')
最易被忽略的一点:Task Worker进程启动时不会自动加载config/autoload/task.php里的全局配置,task_tmpdir、task_max_request这些必须在server.php的settings里显式声明,否则全走Swoole默认值。











