根本原因是仍用“进程级思维”写“协程级代码”:$_get/$_post失效、static单例污染、阻塞函数卡死协程、事务与连接池配置不当,必须转向协程上下文隔离与非阻塞io。

传统 PHP 开发者直接套用 ThinkPHP 或 Laravel 的写法到 Hyperf,90% 会遇到请求参数取不到、数据库查不到、服务启动后卡死或 404 —— 根本原因不是代码写错了,而是还在用「进程级思维」写「协程级代码」。
协程里没有 $_GET 和 $_POST
Hyperf 中 $_GET、$_POST、$_SERVER 全部失效,不是框架故意不支持,而是协程模型下它们无法保证线程安全。每个请求运行在独立协程中,全局变量会被多个协程交叉覆盖。
- 正确做法是通过
RequestInterface获取:在控制器方法参数中声明RequestInterface $request,再调用$request->get('id')或$request->input('name') - 若需在工具函数里访问请求数据,必须从协程上下文显式获取:
Context::get(ServerRequestInterface::class),不能依赖任何全局变量 - 别在中间件外直接读
$_GET,哪怕只是临时调试——它可能返回上一个协程残留的值
单例对象不能再靠 static 存
传统 PHP 常用 private static $instance 实现单例,但在 Hyperf 协程中,这个 static 是进程级共享的,所有协程共用同一个实例,导致状态污染(比如用户 A 登录后,用户 B 的请求看到的是 A 的 session 数据)。
- 要用协程隔离的单例:通过
ApplicationContext::getContainer()->get(YourService::class)获取,Hyperf 的 DI 容器默认按协程生命周期管理实例 - 如果必须手动管理,用
Context::set()和Context::get()绑定到当前协程上下文,而不是static变量 - 特别注意 Redis、MySQL 连接、HTTP 客户端等资源类,绝不能用
new后存为static,否则连接复用错乱,触发MySQL server has gone away
sleep()、file_get_contents()、curl_exec() 全部禁用
这些函数是同步阻塞的,一旦执行,整个协程就卡住,其他并发请求全部被拖慢 —— 这是 Hyperf 启动后 QPS 不升反降的最常见原因。
- 替代
sleep():用co::sleep(1.5)(单位秒,支持浮点) - 替代
file_get_contents():用Hyperf\HttpClient\Client的get()方法,它底层走协程 DNS + 协程 TCP - 替代
curl_exec():同上,或使用Hyperf\Guzzle\CoroutineHandler包装 Guzzle - 别在协程里调
exit或die,它会终止整个 Worker 进程,不是只结束当前请求
事务和连接池配置必须显式对齐
Hyperf 默认启用连接池,但 DB::transaction() 如果没配好,很容易出现「刚插入就查不到」「事务不回滚」—— 表面是 ORM 问题,实际是协程连接复用逻辑没理解透。
-
config/autoload/database.php中pool.min_connections别设为 0,开发时至少设 1,否则首次请求可能因无可用连接而超时 - 读写分离场景下,
DB::table()->insert()后立刻->where()->first()查不到,是因为读库有主从延迟;要强制走写库,加->useWritePdo() - 手动开启事务时,
beginTransaction()必须和commit()/rollback()在同一协程内执行,跨协程调用会导致连接归属混乱
最难的不是学新 API,而是把「一次请求 = 一个进程」的肌肉记忆,替换成「一次请求 = 一个协程上下文容器」—— 所有数据绑定、资源持有、生命周期控制,都得围着这个上下文转。漏掉任意一环,性能优势就变成稳定性黑洞。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











