hyperf协程即swoole协程,直接复用其调度器与上下文机制;启动时调用runtime::enablecoroutine()并设置钩子,确保i/o协程化;请求通过context绑定serverrequestinterface到协程id;task_worker、pcntl_fork、未hook的sleep等会导致协程环境丢失。

Hyperf协程就是Swoole协程,没有独立实现,也不需要额外抽象层。它直接复用 Swoole 的 Swoole\Coroutine 调度器和上下文机制,Hyperf 只做封装、适配和生命周期管理。
Hyperf 启动时如何接管 Swoole 协程
Hyperf 在服务启动时调用 Swoole\Runtime::enableCoroutine(),并设置 SWOOLE_HOOK_ALL(或按需启用子集),让 PHP 原生函数(如 mysql_connect、file_get_contents、curl_exec)自动转为协程版本。这个钩子必须在 Server::start() 之前完成,否则后续协程 I/O 会退化为同步阻塞。
- 检查点:确认
config/autoload/coroutine.php中enable为true,且flags包含关键钩子(例如SWOOLE_HOOK_TCP+SWOOLE_HOOK_FILE) - 常见错误:
SWOOLE_HOOK_CURL在较新 Swoole 版本中已废弃,改用hyperf/http-client或Swoole\Coroutine\Http\Client - 陷阱:若在协程外(如
onWorkerStart回调里)执行了未 hook 的阻塞操作,整个 worker 进程会被卡住
Hyperf 如何保证每个请求都在独立协程上下文中运行
Hyperf 不依赖 PHP 全局变量(如 $_GET、$_SERVER),而是把 ServerRequestInterface 实例绑定到当前协程 ID(cid),通过 Hyperf\Context\Context::set() 存储,再由 @Inject 或 Context::get() 按需提取。这个过程完全基于 Swoole 的 co::getuid() 和用户态上下文数组。
- 典型场景:中间件里获取请求头,必须用
Context::get(ServerRequestInterface::class),不能用$_SERVER['HTTP_X_TOKEN'] - 静态变量陷阱:类内
private static $cache = []是进程级共享的,不同协程会互相污染;应改用Context::set('my_cache', [...]) - 性能影响:
Context查找是 O(1) 数组访问,但滥用嵌套set/get(比如每毫秒调用数十次)会增加微小开销
什么时候会意外脱离 Swoole 协程环境
最隐蔽的问题不是“没开启协程”,而是“协程被意外销毁或未正确延续”。典型情况包括:
- 在
task_worker中执行业务逻辑——它运行在独立进程,无协程上下文,Context::get()返回null,Db::select()会报错“not in coroutine” - 使用
pcntl_fork()或exec()等系统调用,子进程不继承父协程状态 - 异步信号处理(
swoole_signal_add())回调里未手动创建协程,直接写日志或查 DB 就会崩 - 第三方库内部调用
sleep()或stream_select()且未被 hook,导致当前协程挂起,但调度器无法感知,等效于阻塞
真正难调试的从来不是“协程怎么开”,而是“协程在哪断了”。建议上线前用 Swoole\Coroutine::getCid() 在关键路径打点,确认 cid 始终非零且连续变化。











