必须替换响应、数据库、路由等同步组件:禁用yii::$app->response->send(),改用$response->end();mysql/redis换co\mysql/co\redis;重写urlmanager和actionfilter;日志调试等均需协程安全替代。

Yii 框架本身不原生支持 Swoole,所谓“Swoole 支持”本质是绕过 PHP-FPM 生命周期,用 Swoole 启动常驻进程来托管 Yii 应用——但直接跑原版 Yii 代码会出错,必须改响应、换数据库驱动、重写路由逻辑。
禁用 Yii::$app->response->send() 才能避免空白页或 500
常见错误现象:页面完全空白、返回 500、Swoole 日志里出现 WARNING swManager_check_exit_status: worker#0 abnormal exit, status=0, signal=11 或 cross-coroutine write response。
根本原因是 Swoole 协程 HTTP 服务器要求所有响应必须在当前协程内完成,而 Yii 原生的 Response::send() 会跳过协程调度器直写 socket,触发底层保护机制。
- 必须显式禁用
Yii::$app->response->send()和Yii::$app->end() - 控制器中改用 Swoole 的
$response->end($content)或$response->write($chunk) - 若用
yii\web\View渲染模板,先调用$view->renderFile()得到字符串,再传给$response->end() - 绝对不要在协程里调用
exit、die、header()等原生输出控制函数
co\MySQL 和 co\Redis 不是自动替换,PDO 和 phpredis 仍会阻塞协程
常见错误现象:QPS 上不去、并发一高就卡住、CPU 占用低但响应延迟飙升——说明 I/O 还在同步阻塞。
swoole-yii2 扩展只接管了 HTTP 生命周期,不会自动把 yii\db\Connection 或 yii\redis\Connection 替换为协程版。你写的 AR 查询(如 User::find()->one())底层仍是 PDO,照样阻塞整个协程。
- MySQL 必须用
co\MySQL(Swoole 4.5+)或swoole_mysql,不能用 PDO - Redis 必须用
co\Redis,不能用phpredis或predis - 每次协程内需独立
new co\MySQL()并connect(),禁止复用连接对象(Swoole 不允许跨协程共享资源) -
co\MySQL::query()返回数组,不是yii\db\Command对象,AR 链式调用(->where()->orderBy()->all())全部失效,得手写 SQL
UrlManager 和 ActionFilter 在 Swoole 下完全失效,必须重写
常见错误现象:路由 404、createUrl() 生成错误 URL、beforeAction() 完全没执行、CSRF token 校验失败。
因为 Swoole 的 $request 是对象,字段名和结构跟 PHP-FPM 下的 $_SERVER 完全不同(比如路径在 $request->server['request_uri'],不是 $_SERVER['REQUEST_URI']),而 Yii 的 UrlManager::parseRequest() 重度依赖 $_SERVER。
- 路由解析必须从
$request->server['request_uri']和$request->server['request_method']提取,再交由自定义分发器处理 -
ActionFilter::beforeAction()不会被自动调用,需在协程入口手动包裹逻辑(例如在RequestDispatcher中提前执行权限检查) - 日志、调试工具(如
yii\debug\Module)也需协程安全替代,否则可能写乱文件或死锁
真正难的不是启动一个 Swoole 进程,而是把 Yii 里所有隐式依赖 PHP-FPM 生命周期的组件(响应、路由、DB、Redis、日志、调试、Session)一个个拆出来,替换成协程安全的等价实现——漏掉任何一个,都可能在高并发下突然崩掉,而且问题很难复现。










