Hyperf在Docker中server:watch不触发proxy重建,因其仅监听文件变更并重启Worker,不执行注解扫描与di:init-proxy,需手动清空runtime/container、runtime/proxy、runtime/cache三目录。

为什么Docker里server:watch不触发proxy重建
Hyperf 的 server:watch 在容器中只做文件变更监听和进程重启,它完全不调用 di:init-proxy 或重新执行注解扫描。即使你改了 #[Controller],只要 runtime/proxy/ 下的代理类没被覆盖,框架就继续加载旧类——而旧类里根本没有新注解信息。
常见错误现象:
- 新增路由后访问 404
- 删掉
#[Aspect]类,切面逻辑仍在执行 - 修改
dependencies.php后报must not be accessed before initialization
Docker 环境下更麻烦:overlay2 文件系统对 inotify 支持不稳定,watcher 可能压根收不到事件;即使收到,也只杀 worker,不清理缓存目录,也不重跑 DI 初始化流程。
必须清空的三个 runtime 子目录
别只删 runtime/container。在 Docker 中,proxy 缓存可能分散在多个位置,且不同 Hyperf 版本行为不一致。以下路径必须全部清空:
-
runtime/container:注解元数据、AOP 切点映射、DI 容器定义全在这里 -
runtime/proxy:代理类代码(如Proxy\App\Controller\IndexController.php) -
runtime/cache:部分组件(如 Config、Validator)会写入序列化缓存,尤其启用cacheable时
推荐命令(避免误删 runtime/logs):
rm -rf runtime/container runtime/proxy runtime/cache
注意:rm -rf runtime/* 在 Windows + Docker 挂载卷场景下容易因权限失败,显式指定子目录更可靠。
ScanCache 机制不是“自动生效”,而是“启动时跳过扫描”
SCAN_CACHEABLE=true 不是让热更新变快,它是告诉 Hyperf:“如果 runtime/container/annotation/ 里有现成的 PHP 数组,就直接 include,别再反射遍历所有类”。但它不会帮你生成这些文件——首次生成必须靠 di:init-proxy 手动触发。
开发阶段在 Docker 中启用 ScanCache 的正确姿势:
- 确保
config/autoload/annotations.php中'scan' => ['enable' => true] - 在
.env或config/autoload/constants.php中设SCAN_CACHEABLE=true - 容器首次启动前,执行
php bin/hyperf.php di:init-proxy(可放在 Dockerfile 的ENTRYPOINT前) - 后续热更新仍需清空三个目录 + 重启,ScanCache 只加速重启后的加载阶段
如果跳过 di:init-proxy,SCAN_CACHEABLE=true 会直接导致启动失败或注解全失效——因为没有缓存文件可读。
Docker 中真正可行的热更新方案
别指望 server:watch 在生产级容器里稳定工作。它依赖 inotify,在 overlay2、rootless Docker、CI 构建机上大概率失灵。更实际的做法是:
- 开发镜像中挂载源码,并用
docker run -v $(pwd):/app+server:watch,但每次改完必须手动清缓存三目录 - 把清缓存 + 重启封装成一键脚本(如
reload.sh),避免漏删 - CI/CD 流程中禁用 watch,改用构建时执行
di:init-proxy+ 启动常驻进程 - 需要动态配置?走 Nacos/Redis PubSub,而不是碰热更新开关
最易被忽略的一点:Windows + Docker 桌面版下,文件变更通知延迟可达数秒,且 Finder 遍历顺序不稳定——当类数量 >60 时,di:init-proxy 可能漏扫靠后的枚举类,导致 #[OA\Property] 失效。这种问题不会报错,只会静默跳过。


















