Laravel基于模型关联删除队列任务及相关管理需求咨询
Laravel Redis队列任务管理方案(对应你的三个需求)
1. 删除特定模型ID关联的待处理任务
Redis驱动的队列里,待处理任务分两种:普通队列(queues:{队列名})和延迟队列(queues:{队列名}:delayed)。要删除指定模型ID的任务,需先给邮件任务打上标识,再扫描Redis队列匹配删除。
第一步:给邮件类添加模型ID属性
确保你的Mailable类携带模型ID,这样序列化后能被识别:
namespace App\Mail; use Illuminate\Mail\Mailable; class ReservationMail extends Mailable { public $modelId; public function __construct(int $modelId) { $this->modelId = $modelId; } public function build() { return $this->view('emails.reservation'); } }
第二步:扫描并删除匹配任务
写一个工具方法(可封装成Artisan命令方便调用):
use Illuminate\Support\Facades\Redis; use App\Models\Reservation; function purgeTasksForModel(int $modelId, string $queue = 'default') { // 处理普通待处理队列 $queueKey = "queues:$queue"; $tasks = Redis::lrange($queueKey, 0, -1); foreach ($tasks as $taskJson) { $payload = json_decode($taskJson, true); if ($this->isMatchingTask($payload, $modelId)) { Redis::lrem($queueKey, 1, $taskJson); } } // 处理延迟队列 $delayedKey = "queues:$queue:delayed"; $delayedTasks = Redis::zrange($delayedKey, 0, -1, true); foreach ($delayedTasks as $taskJson => $timestamp) { $payload = json_decode($taskJson, true); if ($this->isMatchingTask($payload, $modelId)) { Redis::zrem($delayedKey, $taskJson); } } // 同步删除数据库关联记录(对应手动删除任务的场景) Reservation::where('id', $modelId)->delete(); } // 辅助方法:判断任务是否匹配目标模型ID private function isMatchingTask(array $payload, int $modelId): bool { if (!isset($payload['data']['commandName'], $payload['data']['command'])) { return false; } // 只处理邮件队列任务 if ($payload['data']['commandName'] !== \Illuminate\Mail\SendQueuedMailable::class) { return false; } // 反序列化任务,提取模型ID(仅处理自身任务,避免安全风险) $command = unserialize($payload['data']['command']); return $command->mailable->modelId === $modelId; }
2. 记录任务实际执行时间戳
利用Laravel的队列事件监听,在任务执行完成后更新数据库记录。
注册事件监听器
在App\Providers\EventServiceProvider的$listen数组里添加:
protected $listen = [ \Illuminate\Queue\Events\JobProcessed::class => [ \App\Listeners\LogJobExecution::class, ], ];
创建监听器类
生成监听器:php artisan make:listener LogJobExecution,然后修改内容:
namespace App\Listeners; use Illuminate\Queue\Events\JobProcessed; use App\Models\Reservation; class LogJobExecution { public function handle(JobProcessed $event) { $payload = $event->job->payload(); // 过滤非邮件任务 if (!isset($payload['data']['commandName']) || $payload['data']['commandName'] !== \Illuminate\Mail\SendQueuedMailable::class) { return; } // 提取模型ID并更新执行时间 $command = unserialize($payload['data']['command']); Reservation::where('id', $command->mailable->modelId) ->update(['executed_at' => now()]); } }
3. 邮件未发送时删除关联记录
邮件未发送的场景分两种:任务执行失败(重试耗尽后)、手动删除待处理任务。
处理任务失败场景
监听JobFailed事件,在EventServiceProvider里添加:
protected $listen = [ // 之前的JobProcessed \Illuminate\Queue\Events\JobFailed::class => [ \App\Listeners\CleanupFailedJob::class, ], ];
生成监听器:php artisan make:listener CleanupFailedJob,修改内容:
namespace App\Listeners; use Illuminate\Queue\Events\JobFailed; use App\Models\Reservation; class CleanupFailedJob { public function handle(JobFailed $event) { $payload = $event->job->payload(); if (!isset($payload['data']['commandName']) || $payload['data']['commandName'] !== \Illuminate\Mail\SendQueuedMailable::class) { return; } $command = unserialize($payload['data']['command']); Reservation::where('id', $command->mailable->modelId)->delete(); } }
处理手动删除任务场景
在需求1的purgeTasksForModel方法里,已同步删除数据库关联记录,无需额外操作。
注意事项
- 测试时可通过
Redis::keys('queues:*')查看所有队列键,再用Redis::lrange('queues:default', 0, -1)查看任务内容,确认匹配规则是否正确。 - 若使用多队列,需遍历所有队列键(通过
Redis::keys('queues:*')过滤),或指定目标队列名称。 - 反序列化任务时,确保Mailable类的属性未修改,否则会触发序列化异常。
内容的提问来源于stack exchange,提问作者Cat Named Dog
相关产品推荐
相关产品推荐

