symfony 的 filecache 在 frankenphp 下会锁冲突,因为其多 worker 进程通过 flock() 争抢同一缓存文件的进程级排他锁,导致阻塞、500 错误;需显式隔离 lock_path 或改用 redis/memcached 分布式缓存。

为什么 Symfony 的 FileCache 在 FrankenPHP 下会锁冲突
FrankenPHP 默认启用多 worker 进程(通过 CADDY_WORKERS 或配置中的 workers 控制),而 Symfony 的 FileCache(如 FilesystemAdapter)底层用的是 PHP 的 flock(),它在 Linux 上对同一文件的排他锁是**进程级**而非“线程级”——多个 FrankenPHP worker 同时尝试写入同一个缓存文件(比如 var/cache/prod/annotations.php)时,flock() 会阻塞甚至超时失败,表现为页面卡顿、500 错误或日志里反复出现 Unable to acquire file lock。
FilesystemAdapter 的 lock_path 参数必须显式隔离
Symfony 6.4+ 允许为 FilesystemAdapter 指定独立的锁目录,否则所有 worker 共享默认锁路径(var/cache/* 下的子目录),加剧争抢。你得在 cache.yaml 中强制拆分:
framework:
cache:
pools:
app.cache:
adapter: cache.adapter.filesystem
provider: 'cache.provider.filesystem'
# 关键:每个 worker 必须有独立锁路径
options:
lock_path: '%kernel.cache_dir%/pool_%env(int:CADDY_WORKER_ID)%_lock'
注意:%env(int:CADDY_WORKER_ID)% 是 FrankenPHP 注入的环境变量,表示当前 worker 编号;若未启用 worker ID(比如用 static 模式),可改用 getmypid() 动态生成,但需确保目录可写。
更推荐直接切换到 RedisAdapter 或 MemcachedAdapter
File-based 缓存天生不适合常驻内存 + 多进程模型。FrankenPHP 原生支持 Redis(Docker 镜像已内置 php-redis 扩展),改用分布式缓存既消除了锁问题,又提升一致性:
- 安装 Redis 服务(Docker Compose 中加
redis:服务) - 在
cache.yaml中定义新池:app.cache: adapter: cache.adapter.redis provider: 'redis://redis:6379' - 把
cache.app切到该池:pool: app.cache
这样所有 worker 共享同一 Redis 实例,无文件锁、无路径权限问题,且支持缓存自动过期和原子操作。
别忽略 var/cache 目录的写权限与挂载方式
即使用了 Redis 缓存,Symfony 启动阶段仍需写 var/cache(如容器编译、路由缓存)。在 Docker 环境中,如果 var/cache 被 volume 挂载为共享目录(比如 ./var/cache:/app/var/cache),多个 FrankenPHP worker 会同时尝试写入同一宿主机路径,flock() 在 NFS 或某些 overlayfs 下行为不可靠,极易出错。
解决办法只有两个:
- 禁用共享挂载,让每个容器独占
var/cache(推荐) - 若必须共享(如开发热重载),改用
tmpfs挂载:tmpfs: /app/var/cache,避免磁盘 I/O 和锁竞争
真正麻烦的从来不是“换缓存驱动”,而是没意识到 FrankenPHP 的多进程本质已经让传统单机文件缓存失效——它不兼容,不是配置没调好。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











