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

协程化 Yii2 应用必须替换 Yii::$app->response 的写法
默认的 Yii2 响应流程是同步阻塞的,直接在协程里调用 $response->send() 会触发 Swoole 的“跨协程写响应”警告,甚至导致空白页或 500。根本原因是 Swoole 的协程 HTTP 服务器要求所有响应操作必须在当前协程上下文中完成,而原生 Response::send() 会绕过协程调度器直接写 socket。
实操建议:
- 禁用
Yii::$app->response->send()和Yii::$app->end(),它们在协程模式下不可用 - 改用
SwooleHttpServer提供的$response->end($content)或$response->write($chunk)手动输出 - 确保控制器中不依赖
exit、die或header()等原生 PHP 输出控制函数 - 若使用
yii\web\View渲染模板,需先调用$view->renderFile()获取字符串,再传给$response->end()
swoole-yii2 扩展不自动协程化数据库和 Redis 客户端
这个扩展只接管了 HTTP 生命周期(启动、路由、响应),但不会自动把 yii\db\Connection 或 yii\redis\Connection 替换为协程版驱动。如果你继续用 PDO 或 phpredis,所有 I/O 仍会阻塞整个协程,失去并发优势。
实操建议:
- MySQL 必须切换到
swoole_mysql或co\MySQL(Swoole 4.5+ 推荐);PDO 不支持协程 - Redis 必须用
co\Redis,不能用yii\redis\Connection默认的phpredis或predis - 数据库连接池需手动管理:协程内每次请求都应
new co\MySQL()并connect(),不要复用连接对象(Swoole 不允许跨协程共享资源) - 注意
co\MySQL::query()返回的是数组而非yii\db\Command对象,原有 AR 查询链式调用全部失效
路由和中间件无法直接复用 yii\web\UrlManager 和 yii\base\ActionFilter
swoole-yii2 启动的是独立的 Swoole HTTP 服务,不走 Apache/Nginx + PHP-FPM 那套生命周期,所以 UrlManager 的 parseRequest() 和 createUrl() 在协程上下文中行为异常——它依赖全局 $_SERVER,而 Swoole 的 $request 是对象,字段名也不同(如 $request->server['request_uri'] 而非 $_SERVER['REQUEST_URI'])。
实操建议:
- 路由解析必须重写:从
$request->server['request_uri']和$request->server['request_method']中提取路径,再交由自定义分发逻辑处理 -
ActionFilter的beforeAction()/afterAction()不会被自动调用,需在协程入口手动包裹逻辑 - CSRF、Session 等依赖
$_COOKIE/$_SESSION的组件全部失效,必须用co\http\Server的$request->cookie和基于 Redis 的协程安全 session 实现 - 日志写入需避开
file_put_contents()同步写法,改用co\Channel投递到协程日志 worker
调试时 var_dump 和 error_log 会卡死协程
在协程中直接调用 var_dump() 或 error_log(),尤其是输出大数组或对象时,会触发 PHP 的同步 I/O,让当前协程挂起,影响其他并发请求。更隐蔽的问题是:Swoole 的 enable_coroutine 默认开启后,部分系统函数(如 gethostbyname)已被协程化,但 var_dump 没有,它底层仍走 stdout 阻塞写。
实操建议:
- 调试变量请用
Co::printDebug($data)(Swoole 4.8+)或print_r($data, true)+$response->write()输出到 HTTP 响应体 - 错误日志统一走
swoole_error_log配置项,或投递到co\Channel由专用协程消费 - Xdebug 完全不可用——它与协程调度器冲突,启用即崩溃;只能靠
echo+microtime(true)手动打点 - 别在协程里
sleep(1),要用Co::sleep(1);同理,file_get_contents必须换成co\http\Client
真正难的不是加扩展,而是把 Yii2 当成一个“胶水层”,只保留路由分发和业务逻辑组织能力,把 I/O 密集操作全部下沉到 Swoole 原生协程 API。任何想“无感迁移”的念头,都会在第一个 PDO::query() 调用时被打断。











