yii2+swoole热重载本质是worker进程重启+应用代码重加载,需在onworkerstart中完整重建application实例并重置db连接,监听源码目录排除vendor/runtime,配合max_wait_time与max_request保障平滑重启。

热重载不是 reload() 一下就完事
Yii2 + Swoole 的热重载,本质是「Worker 进程重启 + 应用代码重新加载」,swoole_server->reload() 只负责发信号、杀旧 Worker、拉新 Worker,它不自动重载 Yii2 配置或容器——你得自己在 onWorkerStart 里重建 yii\web\Application 实例,否则改了控制器、路由或配置文件,完全没反应。
- 常见错误现象:
reload.sh执行后日志显示 “Worker started”,但请求结果仍是旧逻辑,get_included_files()查看发现config/web.php或controllers/SiteController.php根本没被重新 require - 必须把整个 Yii2 启动链(autoload、Yii::setAlias、new Application)全写进
onWorkerStart,不能只 new 一次丢在全局变量里 - 注意
Yii::$app是单例,每次onWorkerStart中 new Application 后,Yii::$app会被覆盖,但旧 Worker 里的引用不会自动失效——所以别缓存$app到闭包外或静态属性
文件监控该监听哪些路径?别扫 vendor 和 runtime
用 inotifywait 或 fswatch 监控源码变动时,如果把 vendor/、runtime/、web/assets/ 也加进去,会导致频繁误触发 reload,甚至因并发 reload 导致 Worker 启动失败。
- 推荐监听范围:
src/、controllers/、models/、config/、views/—— 即所有可能被require_once加载的 PHP 文件所在目录 - 必须排除:
--excludei '(\.git|vendor|runtime|cache|log|\.swp|\.swo|\~)$',否则 Composer 更新或日志写入都会触发重启 - macOS 用户注意:
fswatch默认不递归监听子目录,要加-r参数;Linux 下inotifywait -m -r才能持续监听嵌套变更
为什么改了数据库配置 reload 后还是连老库?
因为 Yii2 的 db 组件在 Application 构造时就完成了 PDO 初始化,而 onWorkerStart 里 new Application 是“新实例”,但如果你没显式销毁旧连接或重置连接池,Swoole 的协程上下文可能复用上一个 Worker 的连接句柄。
- 关键动作:每次
onWorkerStart中,必须重新构建Yii::$app->db,包括重设dsn、username、password,不能只改 config 数组再传给 new Application - 更稳妥做法:用
Swoole\Coroutine\Pool管理 PDO,并在onWorkerStart中调用$pool->destroy()再新建,避免连接残留 - 验证方法:在
onWorkerStart里加var_dump(Yii::$app->db->dsn),确保输出和 config 一致,而不是沿用上一轮的缓存值
reload_async = true 不代表零感知,max_request 才是兜底关键
reload_async => true 让 Worker 重启异步化,但若某个请求卡在慢 SQL 或 sleep(30) 里,Swoole 会等它超时(默认 max_wait_time = 5 秒)才强杀。这时候用户可能收到 502 或连接中断。
- 必须配对设置:
'max_wait_time' => 3(比最长业务耗时略小) +'max_request' => 1000(防内存泄漏积累) - 开发期可临时关掉
daemonize,方便用ps aux | grep reload_master观察 Worker PID 是否真变了 - 线上禁用文件监控自动 reload,只保留手动
kill -USR1 $(cat swoole.pid),避免部署脚本和监控脚本打架
onWorkerStart 里的那十几行初始化代码。











