踩坑: Horizon 队列任务卡死,无法进入重试问题排查分析过程

问题

队列执行中, 未执行完成发生多次重试,队列反复执行
队列执行中,突然挂起后卡在padding中,无法进入failed阶段,代码执行结束无响应

问题发生

项目中遇到一个队列,执行较久(执行某段脚本大约10分钟左右). 因为不想盯着任务执行,索性加上多次失败重试. 将horizon配置如下, 为了方便测试,我们把超时时间缩短

    'long-task'  => [
        'connection'      => 'redis',
        'queue'           => [QueueNameKeys::EXPORT_TASK],
        'balance'         => 'auto',
        'memory'          => 256,
        'tries'           => 5,
        'timeout'         => 10,
        ],

修改了tries=5,timeout=10随即将测试代码加入测试执行

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class TestTask implements ShouldQueue
{
    use Dispatchable;
    use InteractsWithQueue;
    use Queueable;
    use SerializesModels;

    public function handle()
    {
        while (true) {
            logger()->info('run in handle');
            sleep(1);
        }
    }

    public function failed(\Throwable $exception)
    {
        logger()->info('run in failed:' . $exception->getMessage());
    }
}

将任务派发进入队列,观察执行结果

php artisan tinker
Psy Shell v0.11.4 (PHP 8.1.4 — cli) by Justin Hileman
>>> use App\Jobs\TestTask
>>> TestTask::dispatch()->onQueue('export-task')
=> Illuminate\Foundation\Bus\PendingDispatch {#5500}

这时候发现执行结果为 : 任务停留在Pending Jobs中, 但是日志输出停留在10条 , 等待很久都没有进入重试机制

踩坑: Horizon 队列任务卡死,无法进入重试问题排查分析过程

经过短暂的排查后, 发现 队列超时配置受 redisretry_after 配置影响, 通俗来讲就是,当retry_after配置之后, 队列的timeout只负责中止程序的执行, 重试需要等待 retry_after. 部分小伙伴也会发现,自己的队列有正常进入重试, 那正是因为 retry_after 配置值较小. 达到配置超时后, 队列正常进入重试 .

对这几项配置,我们可以通过下面文章理解队列的retry_after,timeout,backoff

到这里问题似乎已经解决到这里问题似乎已经解决

貌似只需要调整 retry_after 我们就可以完美进入重试
但是这里看着以前的配置 retry_after = 90000陷入沉思. 原来旧业务中有用到队列 基于时间的尝试 , 当时遇到队列反复重启的问题 (达到retry_after时间队列任未消费完毕, 系统会自动重新启动一个此队列, 会造成队列重复执行, 所以官方给的推荐是 retry_after 配置应该等于 队列中最大的 timeout值.) 而我当前的队列就是需要执行特别久 . 这个导致了两个问题

  • 如果retry_after配的小, 我的长任务会被达到超时时间反复执行
  • 如果retry_after配的大, 我的短任务会在需要重试的时候, 等待这个配置时间结束,才能重试

如何解决这个问题

  • 灵活配置 retry_after ?
    查看代码

踩坑: Horizon 队列任务卡死,无法进入重试问题排查分析过程

发现这个配置只在redis链接的时候可以配置
为什么这个配置不可以单独修改,已经有了timeout为什么我还需要retry_after , 这些问题这里暂不在这里讨论

我们简单得到两个方案

  1. 给队列配置不同的redis连接
  2. 手动控制重试

1.给不同队列配置不同redis

/config/queue.php

    'connections' => [
        'redis'             => [
            'driver'       => 'redis',
            'connection'   => 'default',
            'queue'        => env('REDIS_QUEUE', 'default'),
            'retry_after'  => 90,
            'block_for'    => null,
            'after_commit' => false,
        ],
        'redis-horizon-10m' => [
            'driver'       => 'redis',
            'connection'   => 'default',
            'queue'        => env('REDIS_QUEUE', 'default'),
            'retry_after'  => 600,
            'block_for'    => null,
            'after_commit' => false,
        ],
    ],

/config/horizon.php

   'long-task' => [
                'connection'      => 'redis-horizon-10m',
                'queue'           => [QueueNameKeys::EXPORT_TASK],
                'balance'         => 'auto',
                'memory'          => 256,
                'tries'           => 3,
                'timeout'         => 10,
            ],

2.所有队列不配置重试次数, 再failed方法中定义自己的重试规则

3.您的其他骚操作(谢谢分享)

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 1年前 自动加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 4

这是什么样的业务呀。需要执行10分钟 多少数据量?

1年前 评论

10分钟的任务,能不能用到 任务链

1年前 评论

其实我再业务中更想用 Artisan::call('xxxx') 去处理 . 但是没有发现如何后台调用 , 所以用队列去处理

1年前 评论

此文甚好,解决了我最近的难题。因为发送的短信验证码有时候就进入到pending jobs,在然后就到了failed jobs,提示超时。现在结合文章里说的,的确就是retry_after设置的时间过大,超过了timeout,即时重试任务,也成了超时了。

5个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!