You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 10事件监听器tries与failed方法不生效问题排查

Laravel 10事件监听器tries与failed方法未生效排查

问题描述

开发Laravel 10项目时,已配置事件UptimeHourlySummaryStatCreation和对应队列监听器StoreUptimeHourlySummaryStat。按Laravel队列机制,设置failed方法后,任务失败应执行该方法而非存入failed_jobs表;同时已将监听器的tries设为1,但数据库显示任务实际重试3次才失败,且failed方法未触发。

可能原因及解决办法

1. 队列连接retry_after配置引发超时重试

监听器使用的database-short-running连接将retry_after设为80秒。若任务执行时未主动释放锁,Laravel会判定任务超时,自动重试任务,此逻辑会绕过监听器的tries配置。

解决方式:

  • 确保任务在retry_after时长内完成;若任务逻辑无法缩短,可适当调大retry_after值,但需同步调整队列Worker的--timeout参数(默认60秒)。
  • 若任务需长时间运行,在任务中主动调用$this->release()释放锁,避免被判定为超时重试。

2. Worker全局重试配置覆盖监听器tries

启动队列Worker时若指定了--tries参数(如php artisan queue:work --tries=3),该全局配置会覆盖监听器类中定义的tries属性。

解决方式:

  • 启动Worker时移除全局--tries参数,让监听器的tries生效:php artisan queue:work database-short-running --queue=statistics-hourly
  • 或在Worker命令中指定与监听器一致的tries值:php artisan queue:work database-short-running --queue=statistics-hourly --tries=1

3. failed方法未触发的额外排查点

  • 确认failed方法参数正确:第一个参数为事件实例,第二个为Throwable实例(当前代码参数无误),可在方法内添加日志写入逻辑,验证是否真的未执行。
  • 若任务因超时被重试而非代码抛出异常,failed方法不会触发,只有当任务达到最大尝试次数后才会执行该方法。

相关代码

事件代码(UptimeHourlySummaryStatCreation.php)

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class UptimeHourlySummaryStatCreation
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    /**
     * Create a new event instance.
     */
    public function __construct(
        public mixed $lastCheckedAt,
        public int $userId,
        public int $monitorId,
        public int $totalResponseDuration = 0,
        public int $totalChecksPerformed = 1,
        public int $totalSuccessfulChecksPerformed = 0,
        public int $totalFailedChecksPerformed = 0
    ) {}

    /**
     * Get the channels the event should broadcast on.
     *
     * @return array<int, \Illuminate\Broadcasting\Channel>
     */
    public function broadcastOn(): array
    {
        return [
            new PrivateChannel('channel-name'),
        ];
    }
}

监听器代码(StoreUptimeHourlySummaryStat.php)

<?php

namespace App\Listeners;

use App\Events\UptimeHourlySummaryStatCreation;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use App\Models\UptimeHourlySummaryStat;
use Carbon\Carbon;
use Throwable;

class StoreUptimeHourlySummaryStat implements ShouldQueue
{
    use InteractsWithQueue;

    /**
     * The name of the connection the job should be sent to.
     *
     * @var string|null
     */
    public $connection = 'database-short-running';

    /**
     * The name of the queue the job should be sent to.
     *
     * @var string|null
     */
    public $queue = 'statistics-hourly';

    /**
     * The number of times the queued listener may be attempted.
     *
     * @var int
     */
    public $tries = 1;

    /**
     * Create the event listener.
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     */
    public function handle(UptimeHourlySummaryStatCreation $event): void
    {
        $statistic = UptimeHourlySummaryStat::firstOrCreate([
            'user_id' => $event->userId,
            'monitor_id' => $event->monitorId,
            'grouped_at' => Carbon::parse($event->lastCheckedAt)->format('Y-m-d H:00:00')
        ]);

        // set count of each check
        $statistic->total_checks_performed = $statistic->total_checks_performed + $event->totalChecksPerformed;
        $statistic->total_successful_checks_performed = $statistic->total_successful_checks_performed + $event->totalSuccessfulChecksPerformed;
        $statistic->total_failed_checks_performed = $statistic->total_failed_checks_performed + $event->totalFailedChecksPerformed;

        // set the response durations
        $statistic->total_response_duration = $statistic->total_response_duration + $event->totalResponseDuration;

        if (!$statistic->total_checks_performed) {
            $statistic->avg_response_duration = 0;
        } else {
            $statistic->avg_response_duration = round($statistic->total_response_duration / $statistic->total_checks_performed);
        }

        // set checked date and save
        $statistic->last_checked_at = Carbon::parse($event->lastCheckedAt);
        $statistic->save();
    }

    /**
     * Handle a job failure.
     */
    public function failed(UptimeHourlySummaryStatCreation $event, Throwable $exception): void
    {
        // ...
    }
}

队列配置信息(queue.php)

// for short running queues
'database-short-running' => [
    'driver' => 'database',
    'table' => 'jobs',
    'queue' => 'short-running',
    'retry_after' => 80,
    'after_commit' => false,
],

'database' => [
    'driver' => 'database',
    'table' => 'jobs',
    'queue' => 'default',
    'retry_after' => 90,
    'after_commit' => false,
],

// for long running queues
'database-long-running' => [
    'driver' => 'database',
    'table' => 'jobs',
    'queue' => 'long-running',
    'retry_after' => 300,
    'after_commit' => false,
],

内容的提问来源于Stack Exchange,提问作者Ryan H

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 17:24:59