crontabdispatcherprocess不会因子进程退出而释放资源,资源释放依赖协程结束后的自动回收:协程终止时栈内变量与对象被gc清理,连接池连接自动归还,但需避免状态污染、显式关闭非hyperf封装资源,并调优系统文件句柄限制。

Hyperf 定时任务子进程(即 CrontabDispatcherProcess)本身不直接执行业务逻辑,而是调度器角色——它启动协程循环扫描任务、触发执行。真正的资源释放发生在任务执行完毕后,关键不在“子进程退出”,而在“协程结束 + 上下文清理 + 对象生命周期终结”。完整逻辑围绕协程安全、状态隔离和连接复用展开,不是靠 kill 进程,而是靠框架自动回收。
协程结束即资源自然释放
Hyperf 的定时任务运行在独立协程中(非新进程),execute() 方法执行完,协程自动退出,其栈内变量、局部对象引用全部销毁:
- 协程内创建的临时数组、字符串、DTO 实例等,随协程终止被 PHP GC 回收
- 未显式
unset()也不影响,协程生命周期天然隔离,不会污染其他任务 - 若任务内用了
Context::set(),建议在finally块中Context::del()清理,避免意外透传(虽多数场景无需,但强依赖上下文时推荐)
连接池与客户端资源由框架统一管理
任务中调用的 Hyperf\HttpClient\Client、Hyperf\DbConnection\Db、Hyperf\Redis\Redis 等,均基于 Swoole 协程客户端,底层使用连接池:
- 每次
make()获取的是池中空闲连接,用完自动归还,不 close 不 disconnect - 连接池本身是常驻内存的单例,但每个连接在协程结束时自动释放回池,无需手动
close() - 若任务中新建了非 Hyperf 封装的 PDO 或 Redis 扩展实例(如
new Redis()),则必须显式$redis->close(),否则连接泄漏
单例服务的状态必须请求级隔离
定时任务也运行在 Worker 进程内,共享容器中所有单例服务。若服务类中保存了属性状态(如缓存结果、临时 ID),会导致跨任务污染:
- ❌ 错误:在
UserService中设private $lastResult;并在execute()中赋值 - ✅ 正确:所有中间状态走方法参数、协程上下文或
ApplicationContext::getContainer()->get()按需获取新实例(如用@Inject(scope=ScopeEnum::PROTOTYPE)) - 特别注意:
Logger、EventDispatcher等无状态服务可放心复用;含状态的 Service 必须设计为无状态或作用域隔离
文件句柄与系统资源需主动配合调优
高频秒级任务(如每秒执行)可能密集发起 HTTP/MySQL/Redis 请求,若系统限制未放开,会出现 Too many open files 或静默丢任务:
- 必须在 systemd unit 文件中设置
LimitNOFILE=65536:65536(/etc/security/limits.conf对 Hyperf 无效) - 检查
ulimit -n启动前是否生效,尤其 Docker 容器需加--ulimit nofile=65536:65536 - 避免在任务中
fopen()大量文件却不fclose();推荐用file_get_contents()(协程版)或封装为流式读取+显式关闭
分布式锁与外部资源清理需显式保障
启用 $mutex(如 Redis 锁)的任务,在异常中断时可能遗留锁:
- Hyperf 默认在
execute()抛出异常后自动释放锁(基于try/finally) - 但若任务中调用阻塞函数(如
sleep())、超长耗时或进程被 kill,则锁可能残留 - 建议:Redis 锁设置合理
expire(如 300 秒),并在任务开头加if (! $this->lock->tryLock()) return;防重入 - 涉及 DB 事务或文件写入的,务必用
try/catch/finally确保 rollback 或 unlink











