在 Hyperf 3.1 WebSocket 服务中使用 MySQL 协程,核心是确保操作运行在协程上下文中、走框架封装的协程连接池、避免同步阻塞调用;WebSocket 连接本身已在 Swoole 协程中运行,只需正确配置和调用即可天然协程化。

在 Hyperf 3.1 的 WebSocket 服务中使用 MySQL 协程,核心是确保数据库操作运行在协程上下文中、走框架封装的协程连接池、避免同步阻塞调用。WebSocket 连接本身就在 Swoole 协程中运行(每个连接对应一个协程),所以只要配置正确、调用方式得当,MySQL 操作天然就是协程化的。
确保协程环境已就绪
WebSocket Server 启动时默认已启用协程(Swoole 或 Swow),但需验证:
-
php --ri swoole输出中包含support coroutines: enabled -
config/autoload/server.php中 WebSocket server 的settings至少包含'enable_coroutine' => true(Hyperf 3.1 默认开启) - 不要在
onOpen/onMessage/onClose回调里手动调用Swoole\Runtime::enableCoroutine()—— 此时协程环境已存在,重复启用无意义甚至报错
使用 DI 获取协程数据库连接
不要 new PDO 或 mysqli,而是通过依赖注入获取 ConnectionInterface:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 在 WebSocket 处理器类(如
App\WebSocket\Handler)构造函数中注入:public function __construct(protected ConnectionInterface $connection) {} - 或在方法内通过
$this->container->get(Connection::class)获取(需继承AbstractController或手动访问容器) - 确保
config/autoload/databases.php中mysql配置的pool参数合理(如max_connections≥ 并发连接数 × 平均查询数)
在 onMessage 等回调中执行协程查询
onMessage 是协程函数,可直接调用 ORM 或 QueryBuilder:
- ✅ 安全写法(自动协程调度):
public function onMessage($fd, $data) { $user = $this->connection->table('users')->where('fd', $fd)->first(); // 或用 Eloquent $message = Message::create(['content' => $data, 'fd' => $fd]); } - ⚠️ 避免在协程中混用同步操作:
- 不要
sleep(1),改用co::sleep(1) - 不要
file_get_contents(),改用HttpClient - 不要
mysqli_connect(),必须走$this->connection
- 不要
事务必须在单个协程内闭环完成
WebSocket 场景常见“发消息→查用户→写日志→更新状态”链路,需事务保障一致性:
- ✅ 正确:
$this->connection->transaction(function () use ($fd, $data) { $user = $this->connection->table('users')->lockForUpdate()->where('fd', $fd)->first(); $this->connection->table('messages')->insert(['fd' => $fd, 'content' => $data]); $this->connection->table('users')->where('fd', $fd)->increment('msg_count'); }); - ❌ 错误:
- 在
onMessage里beginTransaction(),然后go()启动另一个协程做commit()→ 跨协程,事务失效 - 把
$connection实例存到静态变量或全局数组里跨连接复用 → 连接被不同协程争抢,状态混乱
- 在
注意连接生命周期与资源释放
- WebSocket 连接长时存在,但每个请求(
onMessage)仍是短生命周期;连接由连接池自动复用和回收,无需手动 close 或 unset - 若某次
onMessage中触发大量并发查询(如广播给 100 个用户查权限),可用Concurrent控制并发数:$concurrent = new Concurrent(10); // 最多 10 个协程并发 foreach ($targetFds as $fd) { $concurrent->create(fn() => $this->connection->table('users')->where('fd', $fd)->value('role')); } await $concurrent;
基本上就这些。关键不是“怎么连”,而是“别跳出协程上下文”。只要不碰原生同步扩展、不跨协程操作事务、不手动管理连接,Hyperf 的 MySQL 就是开箱即协程。

















