Laravel调度器执行错误任务:PdnsSync触发异常Job求助
问题背景
Laravel Kernel中配置了多个队列调度任务,其中PdnsSync每10分钟执行一次,DismissRiskyUsersScheduler每5分钟执行一次。容器队列worker停机维护重启后出现异常:
PdnsSync任务显示正常入队,但实际执行的是DismissRiskyUsersScheduler- 通过Telescope追踪
PdnsSync调度命令,跳转的却是DismissRiskyUsersScheduler的执行记录
后续将PdnsSync指定到专属队列pdns后,php artisan schedule:list显示调度正常,但手动执行php artisan schedule:test无任务记录,Telescope也无相关日志。仅将类名从App\Jobs\PdnsSync改为App\Jobs\PrivateDnsUpdate,任务立刻恢复正常,需排查该现象的深层原因。
环境信息
- 队列连接:Redis
- Laravel版本:10
- PHP版本:8.2
已尝试的排查操作
- 执行缓存清理命令:
php artisan optimize:clear php artisan cache:clear - 重启所有前端及worker容器
- 重启Redis服务
相关代码片段
Kernel调度配置
protected function schedule(Schedule $schedule): void { $schedule->job(new PdnsSync()) ->everyTenMinutes() ->onOneServer(); $schedule->job(new DismissRiskyUsersScheduler) ->everyFiveMinutes() ->onOneServer(); // many more... }
PdnsSync任务类
class PdnsSync implements ShouldQueue, ShouldBeUnique { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; const CHUNK = 10; protected array $jobs = [ LhgTenantJob::class, AviatarTenantJob::class ]; public function handle(): void { $jobs = $this->getJobs(); if (empty($jobs)) { return; } if (count($jobs) > self::CHUNK) { $jobs = array_chunk($jobs, self::CHUNK); } Bus::batch([$jobs]) ->name('pdns') ->dispatch(); } protected function getJobs(): array { foreach ($this->jobs as $job) { $jobs[] = new $job; } return $jobs ?? []; } }
深层原因分析
从现象和解决方法来看,核心原因集中在任务序列化/缓存的残留冲突,具体可能是以下几种情况:
Redis队列旧序列化数据残留
worker停机时,Redis中可能存在未处理的PdnsSync任务,这些任务以序列化字符串存储。如果之前PdnsSync类有过命名空间/类名变更,或序列化数据出现损坏,重启worker后Laravel反序列化时会错误映射到DismissRiskyUsersScheduler。应用缓存清理不会影响Redis中已存在的队列任务数据,导致旧任务持续干扰新任务执行。ShouldBeUnique特性的缓存键冲突
PdnsSync实现了ShouldBeUnique接口,该特性依赖缓存键保证任务唯一性。如果旧的唯一性缓存键(格式通常为laravel_unique_job:{jobClass}:{uniqueId})未被彻底清理,会导致新的PdnsSync任务被判定为重复而跳过执行。修改类名后,新的缓存键生效,任务得以正常触发。OPcache类映射缓存残留
虽然执行了optimize:clear,但PHP OPcache可能缓存了旧的类名映射关系。如果容器的OPcache配置了持久化存储(比如共享内存未被完全释放),重启容器后仍会残留旧类信息,导致Laravel解析类名时出现错误。类名序列化哈希冲突
Laravel序列化队列任务时,会对类名做标识处理。极端情况下,PdnsSync和DismissRiskyUsersScheduler的序列化标识可能出现哈希碰撞,导致反序列化时错误解析任务类。
验证建议
- 手动清理Redis中对应队列的旧任务数据(默认队列键为
queues:default,专属队列则为queues:pdns),再测试原类名任务 - 清理
ShouldBeUnique相关的缓存键,重新触发任务 - 检查PHP OPcache配置,确保
opcache.enable_cli关闭,或重启服务器彻底清空OPcache缓存
内容的提问来源于stack exchange,提问作者lordisp

