Laravel 8 异步队列任务handle方法中APCU缓存为空问题如何解决
问题原因分析
这既不是Laravel框架导致的Bug,也不是配置错误,是PHP APCu扩展本身的进程隔离机制导致的正常现象,原理如下:
- APCu缓存是进程级别的存储,仅共享给同个运行模式下的同组进程:
- 你的Web请求走的是PHP-FPM/CGI进程组,所有FPM进程共享同一份APCu内存池,所以中间件、任务构造方法(构造逻辑是在Web请求的FPM进程中同步执行的)都能正常读到缓存
- Laravel队列Worker是独立的CLI进程,哪怕开启了
apc.enable_cli=1,也仅允许CLI进程自己使用APCu,它的内存空间和FPM进程的APCu池完全隔离,互不通信。任务入队后,handle方法是在队列Worker的CLI进程中执行的,自然读不到FPM进程写入的缓存数据。
解决方案
方案1:调度任务时直接传递所需缓存数据(最推荐,改造成本最低)
在dispatch任务前,或者在任务的构造方法中直接读取需要的APCu缓存,存入任务的公共属性,handle方法中直接调用属性即可,不需要再读APCu。改造示例如下:
// 中间件代码 { $a = apcu_cache_info(); // 250,000 entries $b = apcu_exists('the:2:0'); // true // 直接把需要的缓存数据提前读出来传给任务 $cacheData = apcu_fetch('the:2:0'); MemoryProvider::dispatch($request->all(), $cacheData); return $next($request); } // MemoryProvider 改造 public function __construct($requestData, $cacheData) { $this->request = $requestData; $this->cacheData = $cacheData; // 缓存数据随任务序列化存入队列 } public function handle() { // 直接使用提前读取的缓存数据即可 var_dump($this->cacheData); }
方案2:更换跨进程共享的缓存服务(适合长期业务迭代)
如果你的场景需要大量跨进程(Web/队列/脚本)共享缓存数据,建议替换APCu为Redis、Memcached这类独立的中心化缓存服务。这类服务不依赖PHP进程,所有业务进程都可以通过客户端连接读取同一份数据,天生适配异步队列、定时任务等场景,也是Laravel官方推荐的缓存驱动方案。
方案3:CLI进程请求Web接口读取APCu(不推荐,仅做参考)
可以单独写一个无需鉴权的内部路由,功能是读取指定APCu键的值返回,队列handle方法中通过CURL请求这个本地接口获取缓存数据。这个方案性能低、存在安全风险,仅适合临时兼容场景,不建议线上使用。
内容的提问来源于stack exchange,提问作者Ebaldino
相关产品推荐
相关产品推荐

