踩坑: Horizon 佇列任務卡死,無法進入重試問題排查分析過程

san7fenlengnuan發表於2022-05-30

問題

佇列執行中, 未執行完成發生多次重試,佇列反覆執行
佇列執行中,突然掛起後卡在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 協議》,轉載必須註明作者和本文連結

相關文章