Laravel 9.x 的队列中间件是专为 ShouldQueue 任务设计的链式处理器,通过 handle($job, $next) 实现执行前后逻辑注入;需显式调用 through() 绑定或结合 Queue::before/after 事件使用,不支持 HTTP 上下文。

在 Laravel 9.x 中,任务队列本身不直接支持“中间件”(Middleware)概念——中间件是 HTTP 请求生命周期中的机制,而队列任务运行在命令行或后台进程(如 php artisan queue:work)中,不经过 HTTP 栈。但你提到的“队列中间件”,实际是指 队列任务的前置/后置钩子机制,Laravel 提供了 Queue Middleware(队列中间件),它是专为 Illuminate\Queue\Jobs\Job 设计的、可链式注册的处理器,用于在任务执行前后注入通用逻辑(如日志、重试控制、权限检查、超时限制等)。
什么是队列中间件(Queue Middleware)
Laravel 9.x 的队列中间件是一个 PHP 类,需实现 handle 方法,接收 $job 和 $next 参数。它不是 HTTP 中间件,也不放在 app/Http/Middleware 下,而是独立定义,作用于单个任务或整个队列连接。
关键点:
- 只对使用
dispatch()推送的 可分发任务类(implements ShouldQueue) 生效;闭包任务不支持。 - 必须通过
->through([...])显式绑定到任务实例,或全局配置到队列连接(需自定义 Worker 启动方式)。 - 执行顺序为先进后出(类似栈),即最后注册的中间件最先执行
handle。
如何定义一个队列中间件
在 app/Jobs/Middleware 目录下创建类(目录可自定义),例如限流中间件:
namespace App\Jobs\Middleware;
use Illuminate\Support\Facades\Redis;
use Closure;
class RateLimited
{
public function handle($job, Closure $next)
{
$key = 'queue:rate_limit:' . $job->getJobId();
if (Redis::exists($key)) {
// 已触发限流,丢弃或延迟重试
$job->release(60); // 延迟 60 秒再试
return;
}
Redis::setex($key, 300, 1); // 5 分钟有效期
$next($job);
}
}
注意:$job 是具体的队列 Job 实例(如 Illuminate\Queue\Jobs\RedisJob),可通过 $job->payload()、$job->attempts() 等方法获取上下文。
如何应用队列中间件
有两种常用方式:
-
绑定到单个任务实例:调用
dispatch()时链式使用through()
ProcessPodcast::dispatch($podcast)
->through([App\Jobs\Middleware\RateLimited::class]);
-
全局绑定到某队列连接(需扩展 Worker):修改
config/queue.php中连接配置,并在启动 worker 时传入中间件数组。Laravel 默认不支持开箱即用的全局队列中间件,需自定义Worker或使用事件监听(如监听JobProcessing事件做前置处理)。
更实用的替代方案是利用 Laravel 内置的 任务事件:
-
Queue::before():任务开始执行前(所有任务统一拦截) -
Queue::after():成功执行后 -
Queue::failing():执行失败时 -
Queue::stopping():worker 停止前
示例(在 AppServiceProvider@boot 中注册):
use Illuminate\Support\Facades\Queue;
Queue::before(function ($event) {
\Log::info('Job starting', [
'job' => get_class($event->job),
'connection' => $event->connectionName,
'queue' => $event->job->getQueue(),
]);
});
Queue::failing(function ($event) {
\Log::error('Job failed', [
'job' => get_class($event->job),
'exception' => $event->exception->getMessage(),
'attempts' => $event->job->attempts(),
]);
});
常见使用场景与建议
-
自动重试控制:中间件中检查失败次数 + 错误类型,决定是否
$job->fail()或$job->release($delay),比$tries属性更灵活。 -
上下文注入:如将当前用户 ID、请求 ID 注入任务上下文(通过
$job->resolveName()或自定义属性),便于追踪。 - 资源预检:检查 Redis 连接、外部 API 可用性,避免任务进入执行阶段才失败。
- 避免滥用:不要在中间件中做耗时操作(如 DB 查询、HTTP 调用),否则会阻塞整个队列消费;优先用事件监听 + 异步补偿。
不复杂但容易忽略:队列中间件无法访问 HTTP 请求对象($request)、Session 或认证状态,因为它们根本不在 Web 生命周期内运行。需要传递的数据,务必在 dispatch 时显式序列化进任务构造参数。


















