首页 > 解决方案 > 调度到队列总是选择默认连接

问题描述

我有一个应用程序运行许多不同类型的作业,这些作业运行几秒钟到一个小时。这需要我有两个单独的队列连接,因为 retry_after 绑定到连接而不是队列(即使它们都使用 Redis,这很烦人,但现在不是问题)。一个连接的 retry_after 为 600 秒,另一个为 3600 秒(或一小时)。我的工作实现ShouldQueue和使用特征Queueable, SerializesModels, Dispatchable, InteractsWithQueue

现在解决问题。我创建了一个TestJob睡眠 900 秒以确保我们通过 retry_after 限制。当我尝试使用以下方法在特定连接上分派作业时: dispatch(new Somejob)->onConnection('redis-long-run') 或者像我以前在作业的构造函数中所做的那样(它总是用来工作):

public function __construct() {
    $this->onConnection('redis-long-run');
}

该作业被队列工作人员拾取,它运行 600 秒,之后工作人员重新启动作业,注意到它已经运行一次并失败。300 秒后,作业成功处理。如果我的工作人员允许多次尝试,重复的作业将并行运行 300 秒。

在我的测试工作中,我还打印出$this->connection确实显示了正在使用的正确连接,所以我的猜测是广播公司完全忽略了它。

我在 Docker 环境中使用 Laravel 5.8.35 和 PHP 7.3。主管处理我的工人。

编辑:我已经确认升级到 Laravel v6.5.1 后行为仍然存在

重现步骤:

  1. 将您的队列驱动程序设置为 Redis。
  2. 在 queue.php 中创建两个不同的 Redis 连接,一个命名redis为 retry_after 为 600,另一个命名redis-long-run为 retry_after 为 3600。在我的情况下,它们也有不同的队列,但我不确定这是此测试所必需的。
    'connections' => [
        'redis' => [
            'driver' => 'redis',
            'connection' => 'default',
            'queue' => env('REDIS_QUEUE', 'default'),
            'retry_after' => 600,
            'block_for' => null,
        ],

        'redis-long-run' => [
            'driver' => 'redis',
            'connection' => 'default',
            'queue' => 'long_run',
            'retry_after' => 3600,
            'block_for' => null,
        ]
    ]
  1. 创建一个小命令来分派我们的测试作业 3 次
<?php

namespace App\Console\Commands;

use App\Jobs\TestFifteen;
use Illuminate\Console\Command;

class TestCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'test:fifteen';

    /**
     * Create a new command instance.
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function handle()
    {
        for ($i = 1; $i <= 3; $i++) {
            dispatch(new TestFifteen($i))->onConnection('redis-long-run');
        }
    }
}
  1. 创建测试作业
<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use function sleep;
use function var_dump;

class TestFifteen implements ShouldQueue
{
    use Queueable, SerializesModels, Dispatchable, InteractsWithQueue;

    private $testNumber;

    public function __construct($testNumber)
    {
        $this->onConnection('redis-long-run');
        $this->testNumber = $testNumber;
    }

    public function handle()
    {
        var_dump("Started test job {$this->testNumber} on connection {$this->connection}");
        sleep(900);
        var_dump("Finished test job {$this->testNumber} on connection {$this->connection}");
    }
}
  1. 运行你的队列工作者。我为这些工作人员使用具有以下配置的主管。
[program:laravel-queue-default]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/artisan queue:work redis --queue=default --tries=3 --timeout=600
numprocs=8
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

[program:laravel-queue-long-run]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/artisan queue:work redis --queue=long_run --tries=1 --timeout=3540
numprocs=8
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
  1. 执行工匠命令php artisan test:fifteen

那么我做错了什么还是应用的连接真的不被尊重?

此外,不能仅根据每个作业或队列决定 retry_after 应该是什么,从而能够REDIS用作我的实际队列驱动程序,背后的设计理念是什么?为什么我不能选择 Redis 作为我的队列处理程序并决定 queue-1 在 60 秒后重试,而 queue-2 在 120 秒后重试?当它们使用完全相同的 Redis 实例和所有东西时,我觉得为此设置两个连接是很不自然的。

无论如何,这里希望有人能对这个问题有所了解。先感谢您。

标签: phplaravellaravel-5.8laravel-6.2

解决方案


据我了解,您的连接始终是redis,因此您应该在调度时指定队列:

dispatch(new TestFifteen($i))->onQueue('long_run');

连接是一个不同的驱动程序,如 redis、sync、AWS 等,并且您的队列是该连接的不同配置或多个作业堆栈。


推荐阅读