hyperf docker下注解扫描慢的根本原因是runtime/container/proxy/和annotation/目录被清空,导致每次启动全量反射扫描;应通过挂载volume或构建阶段执行di:init-proxy并关闭scan.enable来解决。

Hyperf 在 Docker 环境下注解扫描慢,根本不是框架变慢了,而是每次容器重启都丢掉 runtime/container/proxy/ 和 runtime/container/annotation/,导致启动时被迫重新全量反射扫描——这个过程在中大型项目里常耗时数秒,健康检查直接超时。
为什么 Docker 里 runtime 目录总被清空
Docker 容器默认是无状态的,runtime/ 目录若没显式挂载或打包进镜像,每次 docker run 或 docker-compose up 都会从一个干净的文件系统开始。常见错误包括:
-
runtime/被写进.dockerignore,构建时根本没进镜像 - 使用
tmpfs或emptyDir挂载runtime/,容器重启即丢失 - ENTRYPOINT 脚本里执行了
rm -rf runtime/*(比如为了“清理日志”),顺手删掉了代理缓存 - Docker Desktop for Windows 的文件共享机制导致
runtime/写入缓慢,扫描卡在 Finder 遍历环节
两种可靠方案:挂载 vs 提前编译
选哪种取决于你是否能控制构建流程和部署环境:
-
挂载
runtime/到宿主机或 volume:适合开发、CI 测试或无法修改构建流程的场景。确保runtime/container/可写且持久,首次启动生成缓存后,后续重启直接复用。注意 Windows + Docker Desktop 下需关闭“gRPC FUSE”或改用 WSL2 后端,否则filemtime()和遍历性能极差 -
构建阶段提前执行
di:init-proxy:生产推荐。在Dockerfile的构建层中,COPY代码后、EXPOSE前插入:RUN php bin/hyperf.php di:init-proxy
并确认runtime/已被 COPY 进镜像(不要rm -rf runtime)。此时容器启动跳过扫描,耗时从秒级降到毫秒级
必须同步关闭运行时扫描开关
即使缓存已存在,只要 config/autoload/annotations.php 中 'scan' => ['enable' => true],Hyperf 仍会先尝试扫描再 fallback——这步完全多余,还可能因路径不可读触发警告。务必显式设为:
'scan' => [
'enable' => false,
'paths' => [],
],
同时确保环境变量 SCAN_CACHEABLE=true 生效(写进 .env 或 config/autoload/constants.php)。漏掉任一条件,缓存都不会被跳过。
Phar 打包和加密环境的特殊处理
如果你用 box 打 Phar 或代码加密工具,BASE_PATH 会变成 phar:// 协议路径,而默认的 scan.paths 配置(如 BASE_PATH . '/app/Controller')会失效。此时必须:
- 在
config/autoload/annotations.php中改用绝对路径,例如/var/www/app/Controller - 或在 Phar 构建脚本里动态覆盖
scan.paths,确保di:init-proxy能正确定位源码 - 加密后保留
runtime/container/下所有.php缓存文件,并确认它们未被加密工具误删或权限重置
最易被忽略的一点:Docker 多阶段构建时,runtime/ 必须出现在 final 阶段的镜像里,而不是只存在于 builder 阶段——很多人把 di:init-proxy 放在 builder 里,却忘了 COPY --from=builder runtime/ runtime/。











