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

Laravel 5.7 Horizon队列任务未执行/失败及限流问题求助

问题排查与解决方案:Laravel Horizon队列任务挂起/失败问题

从你的描述和配置来看,核心问题是API频率限制与队列并发控制不匹配,加上队列配置的一些冲突,导致任务要么无法执行要么触发API限流失败。下面分点拆解问题并给出具体修复方案:


1. 统一API节流规则,避免多键冲突

你现在的两类任务分别用了zohoprojecttasksget和zohoupdatetask两个Redis节流键,但实际上所有任务都共享同一个API的120秒100次限制。这会导致两类任务各自算限流,加起来很容易超过API的总限制,触发30分钟锁定,同时也会让Horizon的任务调度混乱。

修复方案:

把所有调用该API的任务都统一使用同一个节流键,比如zoho-api-throttle,并且调整允许的请求数为90次/120秒(留10次的缓冲空间,避免刚好卡到100次触发限流):

// 夜间同步任务和按需任务都替换成这个节流逻辑
Redis::throttle('zoho-api-throttle')
    ->allow(90)
    ->every(120)
    ->then(function () {
        // API调用逻辑
    }, function () {
        // 触发限流时,释放任务的时间要合理,建议设置为120秒(刚好等限流周期重置)
        return $this->release(120);
    });

2. 解决Supervisor与Horizon的Worker数量冲突

你的Supervisor配置了numprocs=8,但Horizon的production环境设置了processes=10。Horizon是Laravel的队列管理器,Supervisor只是负责启动Horizon进程,Horizon自身会管理内部的Worker数量。这里的冲突会导致Horizon尝试启动10个Worker,但Supervisor只给了8个进程配额,部分Worker无法正常启动,导致任务挂起。

修复方案:

修改Supervisor配置,把numprocs改为1(只需要启动一个Horizon主进程,Horizon自己管理内部Worker):

[program:laravel-worker]
process_name=%(program_name)s
command=php /home/ubuntu/www/artisan horizon
autostart=true
autorestart=true
user=ubuntu
numprocs=1  # 修改为1
redirect_stderr=true
stdout_logfile=/home/ubuntu/otmas/worker.log

同时,根据你的任务量,调整Horizon的processes为合适的数量,比如6-8个(不要超过API限流能承载的并发数)。


3. 拆分队列优先级,避免批量任务阻塞按需任务

目前所有任务都放在default队列,夜间950次的批量同步任务会占满所有Worker,导致用户触发的按需任务无法及时执行,甚至挂起。

修复方案:

  • 新增两个队列:nightly-sync(夜间批量任务)和user-request(用户按需任务)
  • 修改Horizon配置,给不同队列分配不同的Worker数量和优先级:
'environments' => [
    'production' => [
        'nightly-supervisor' => [
            'connection' => 'redis',
            'queue' => ['nightly-sync'],
            'balance' => 'simple',
            'processes' => 4,  // 分配4个Worker处理批量任务
            'tries' => 3,
            'timeout' => 15,
        ],
        'user-supervisor' => [
            'connection' => 'redis',
            'queue' => ['user-request'],
            'balance' => 'auto',
            'processes' => 4,  // 分配4个Worker处理按需任务
            'tries' => 3,
            'timeout' => 15,
            'priority' => 10,  // 按需任务优先级更高
        ],
    ],
    // 其他环境配置同理调整
],
  • 任务类中指定队列:
// 夜间同步任务
class GetProjectTasks implements ShouldQueue
{
    public $queue = 'nightly-sync'; // 指定队列
    // ... 其他代码
}

// 按需任务
class ReplaceTask implements ShouldQueue
{
    public $queue = 'user-request'; // 指定队列
    // ... 其他代码
}

4. 调整任务重试与释放逻辑,避免无效重试

  • 你的夜间任务设置了$tries = 540,这意味着任务会重试540次,每次释放85秒,总重试时间会超过API的30分钟锁定时间,导致大量无效重试。建议把tries调整为18(30分钟/120秒≈15次,留3次缓冲),释放时间改为120秒。
  • 按需任务的$tries = 140也过高,改为10左右即可,释放时间统一为120秒。

示例修改:

class GetProjectTasks implements ShouldQueue
{
    public $tries = 18;
    public $timeout = 15;

    // ... handle方法中的释放逻辑
    return $this->release(120);
}

5. 优化队列连接配置,避免Worker阻塞

你的Redis队列配置中block_for = 140,这个值是Worker在队列空时等待的时间,建议改为60,避免Worker长时间阻塞:

'connections' => [
    'redis' => [
        'driver' => 'redis',
        'connection' => 'default',
        'queue' => 'default',
        'retry_after' => 460,
        'block_for' => 60, // 修改为60
    ],
],

最后验证步骤

  1. 重启Supervisor:sudo supervisorctl reread && sudo supervisorctl update && sudo supervisorctl restart laravel-worker
  2. 重启Horizon:php artisan horizon:terminate(Horizon会自动重启)
  3. 观察Horizon仪表盘,确认任务分配正常,没有挂起或频繁失败的情况
  4. 查看API调用日志,确认没有触发30分钟锁定的情况

内容的提问来源于stack exchange,提问作者Diego Vargas Sotomayor

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:41:57