一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

ThinkPHP 8.0 如何优雅地集成 Swoole?协程化改造步骤【异步】

时间:2026-07-11 09:37:51 编辑:袖梨 来源:一聚教程网

协程数据库必须禁用PDO,改用swoole类型驱动:需将database.php中'type'设为'swoole',关闭读写分离、禁用预处理模拟,明文配置数据库凭证,并通过DatabasePool接管Db::connect()实现协程隔离连接。

协程数据库必须禁用 PDO,改用 swoole 类型驱动

ThinkPHP 8.0 默认走 PDO,但在 Swoole 协程里它会复用连接、破坏事务隔离、甚至 silently 丢数据。这不是配置没开,而是 PDO 对象本身不支持协程调度——beginTransaction()setFetchMode() 等调用会污染其他协程状态。

必须把 config/database.php 中的 'type' 显式改成 'swoole'(不是 'mysql''pdo'),否则 Db::connect() 拿到的还是同步 PDO 实例。

  • 'type' => 'swoole':强制启用 Swoole 原生协程 MySQL 客户端
  • 'deploy' => 0:关闭读写分离,否则 Connection 类会绕过协程驱动,偷偷 new PDO
  • 'params' => [PDO::ATTR_EMULATE_PREPARES => false]:禁用预处理模拟,否则 SQL 被 PHP 同步拼接,失去协程意义
  • 'hostname''username''password' 必须明文写死,不能用 env() —— 协程上下文不保证环境变量始终可读

Db::connect() 不再返回单例,得靠 DatabasePool 接管

默认 Db::connect() 是单例 + 连接复用,协程里多个请求拿到的是同一个 PDO 对象。你手动 new PDO() 也不行——生命周期难管理,容易漏关、OOM。

真正安全的做法是用 think-swoole 自带的 DatabasePool 替换连接创建逻辑:

立即学习“PHP免费学习笔记(深入)”;

  • app/provider.php 或自定义服务提供者中绑定:Container::getInstance()->bind('db', function($app) { ... });
  • DatabasePool 会按 SwooleCoroutine::getuid() 分配独占连接,每个协程拿到隔离实例
  • 池大小建议设为 max_request * 2(如 Swoole 配置 max_request => 3000,池大小设 60–100);太大易 OOM,太小导致频繁创建销毁

Swoole 配置里 max_coroutine 必须显式设大

如果只开了 enable_coroutine => true 却没设 max_coroutine,协程内发起的异步 DB 查询会被阻塞,Db::transaction() 直接卡死——因为协程数超限后,新协程排队等资源,而事务又锁着连接不放。

  • 'max_coroutine' => 3000 是常见安全值,可根据并发峰值微调
  • 'worker_num' 建议设为 CPU 核数 × 2~4,超过 32 很少带来收益,反而加剧进程间锁竞争
  • 'task_worker_num' 留 2~4 个足够,日志、邮件这类耗时操作才扔进去;Redis 缓存更新本就是协程安全的,别误丢进 task

HTTP 请求入口必须 go 包裹 + 清理上下文

直接在 request 回调里跑 $app->http->run() 会导致请求上下文残留,静态属性、全局变量、Session 数据跨请求污染。

每个请求必须包裹在 go() 里,并显式清理:

  • go(function() use ($app, $request, $response) { ... }) 启动独立协程
  • 响应结束后执行 $app->request->clear()$app->session->flush()
  • 异常必须 try/catch 捕获,否则 worker 进程崩溃,整个服务挂掉

协程安全不是加个 go 就完事,连接池、上下文清理、错误兜底这三块缺一不可——漏掉任意一个,上线后都可能表现为偶发卡顿、事务失败或数据错乱。

热门栏目