enable_coroutine => true 是性能跃迁前提,但仅配置无效;必须在WorkerStart中调用Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL),禁用use_pdo、改type为mysql,手动控制Session并隔离协程上下文。

enable_coroutine 必须设为 true,否则协程根本不会启动——这不是可选项,是性能跃迁的前提。
为什么 enable_coroutine => true 不生效?
常见现象:配置写了、php --ri swoole 显示已加载,但压测时 CPU 仍飙升、并发卡在几百,strace 看到大量 read/write 阻塞调用。
根本原因不是配置漏了,而是 ThinkPHP 的数据库连接在初始化阶段就硬创建了 PDO 实例,绕过了 Swoole 的协程 Hook。Swoole 只能接管运行时新建的连接,对已存在的同步连接无能为力。
- 必须在
WorkerStart回调里调用Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL),不能在全局或配置文件中提前调用(框架初始化可能覆盖它) -
config/database.php中禁用'use_pdo' => true,改用原生 MySQL 协程驱动(type => 'mysql'+think-swoole连接池) - 检查是否误用了
think\db\connector\Mysql::connect()—— 它内部调用的是mysqli_connect,而默认不 Hook,需显式加SWOOLE_HOOK_MYSQLI
Db::transaction() 报 “Cannot use object of type Swoole\Coroutine\MySQL as resource”
这是协程 MySQL 对象和传统事务 API 冲突的典型错误。ThinkPHP 原生事务方法依赖 mysqli_commit() 这类函数式资源操作,但协程客户端返回的是对象实例。
- 禁用
Db::transaction(),改用$db->startTrans()+$db->commit()/$db->rollback()——think-swoole v4+已适配这套接口 - 确保未混用
think-orm和旧版think-db,二者协程事务层实现不一致 - 不要在协程外提前调用
Db::table()->select()触发连接建立,所有 DB 操作必须发生在WorkerStart初始化连接池之后、且在协程内执行
Session 和 Cookie 在 Swoole 下失效或跨请求污染
Swoole 常驻内存导致 PHP 原生 session_start() 锁文件、setcookie() 输出时机错乱,表现为登录态丢失、用户数据串号。
立即学习“PHP免费学习笔记(深入)”;
- 关闭自动 Session:
ini_set('session.auto_start', 0),所有 session 操作改为手动控制:session_id($id); session_start(); - 响应结束前必须调用
session_write_close(),否则后续请求会阻塞等待写锁 - Cookie 不要用
header('Set-Cookie: ...'),改用$response->cookie()(think-swoole提供)或直接操作$server->response->cookie() - Session 存储必须切换为非文件驱动,推荐 Redis,并确保使用
Swoole\Coroutine\Redis实例
连接池没起作用?检查 pool_size 和初始化时机
连接池失效最常被忽略的一点:它只在 WorkerStart 中初始化才有效。如果在配置文件里写死 pool_size 但没在回调里调用 Db::setConfig(),那只是空配置。
- 在
WorkerStart中显式调用:Db::setConfig(['type' => 'swoole', 'pool_size' => 20]) -
pool_size不宜过大(如 >50),单个 Worker 进程内连接数过多反而增加调度开销;建议设为worker_num × 2 ~ 5 - Redis 连接池需配合
co\Redis驱动,在WorkerStart中 new 实例并注入容器,不能复用think-cache默认的同步 Redis 实例
$_GET、$_POST、静态属性、单例状态,全部要在每次请求开始前重置、结束时清理。漏掉任意一项,都可能在高并发下引发数据污染或内存泄漏。



















