hyperf 的热更新是通过 server:watch 启动、文件扫描和进程重启实现的伪热更,而非实时重载;start 命令因无监听和 opcache 缓存导致改代码不生效。

Hyperf 的热更新不是“改完保存就生效”,而是靠 server:watch 启动 + 文件扫描 + 进程重启实现的伪热更;它不 reload 当前进程,而是杀掉旧 worker、拉起新 worker 加载新代码。
为什么 php bin/hyperf.php start 改代码不生效
Hyperf 基于 Swoole,启动后所有 PHP 类被一次性加载并常驻内存。start 命令走的是生产模式,无文件监听逻辑,也不清理 opcache —— 即使你改了 app/Controller/IndexController.php,正在运行的 worker 进程仍执行旧 opcode。
- 错误现象:
curl http://127.0.0.1:9501返回的还是修改前的内容,ps aux | grep hyperf看不到进程重启 - 根本原因:Swoole worker 进程生命周期内不会重新 require 文件,PHP 不像 FPM 那样每个请求都重载脚本
- opcache 会雪上加霜:默认开启时,即使 worker 重启,旧字节码可能仍被复用,必须设
opcache.revalidate_freq=0或禁用
用 server:watch 实现开发期自动重启
Hyperf 官方 hyperf/watcher 组件是开发阶段最轻量、最可控的热更方案,本质是轮询扫描文件变更后执行 kill -USR1 $master_pid + 新进程拉起。
- 安装:
composer require hyperf/watcher --dev - 发布配置:
php bin/hyperf.php vendor:publish hyperf/watcher(生成config/autoload/watcher.php) - 关键配置项:
-
watch.dir默认为['app', 'config'],如需监听.env,需手动加入 -
ext默认含.php和.env,但修改.env不触发重启,需额外处理 -
watch.interval设为1000可加快响应,但会略增 CPU 占用
-
- 启动命令:
php bin/hyperf.php server:watch(不是start)
server:watch 启动失败或不触发重启的常见原因
不是装了 watcher 就万事大吉。很多问题卡在信号链路或环境配置上。
- 没关 opcache:
php -i | grep opcache.enable必须返回off,或确保opcache.revalidate_freq=0且opcache.validate_timestamps=1 - 监听路径不对:
watch.dir写成./app或绝对路径会失效,应保持默认app(相对项目根目录) - 权限问题:Linux 下若用 root 启动 watcher,但项目文件属主是普通用户,inotify 可能无法监听变更
- 文件未真正写入完成:编辑器(如 VS Code)保存时可能先写临时文件再 mv,watcher 扫描到的是中间态;建议设
watch.interval >= 1500并避免高频保存 - 忽略
vendor/和runtime/是硬性要求:否则composer install或日志写入会疯狂触发 reload
别把 server:watch 当成生产热更方案
它只适合本地开发。生产环境必须用 max_request + USR1 信号 + 进程管理工具组合,否则会导致不可控的并发重启和连接中断。
-
server:watch会频繁 fork 新进程,没有优雅关闭长连接机制,不适合 WebSocket 或 SSE 场景 - 它依赖轮询,有延迟(最小 1s),不如 inotify/fswatch 实时,但胜在跨平台、免系统依赖
- 真正要上线,得切回
start,并在config/autoload/server.php中设'max_request' => 10000,配合部署脚本发kill -USR1 $(cat runtime/swoole.pid) - 所有全局静态变量、
static属性、单例容器状态,在 worker 重启后都会丢失 —— 别在onWorkerStart里缓存 DB 连接以外的东西











