如何暂停 Laravel 队列

How do you pause a Laravel queue

我有一个向远程服务发送请求的队列。有时此服务会进行维护。当遇到这种情况时,我希望所有队列任务都暂停并在 10 分钟后重试。我该如何实施?

<?php

namespace App\Jobs;

use ...

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

    const REMOTE_SERVER_UNAVAILABLE = 'remote_server_unavailable';

    private $msg;
    private $retryAfter;

    public function __construct($msg)
    {
        $this->msg = $msg;
        $this->retryAfter = 10;
    }

    /**
    * Execute the job.
    *
    * @return void
    */
    public function handle(){
        try {
            // if we have tried sending the request and get a RemoteServerException, we will
            // redispatch the job directly and return.
            if(Cache::get(self::REMOTE_SERVER_UNAVAILABLE)) {
                self::dispatch($this->msg)->delay(Carbon::now()->addMinutes($this->retryAfter));
                return;                  
            }
            // send request to remote server
            // ...
        } catch (RemoteServerException $e) {
            // set a cache value expires in 10 mins if not exists.
            Cache::add(self::REMOTE_SERVER_UNAVAILABLE,'1', $this->retryAfter);
            // if the remote service undergoes a maintenance, redispatch a new delayed job.
            self::dispatch($this->msg)->delay(Carbon::now()->addMinutes($this->retryAfter));            
        }
    }
}

您可以使用 Queue::looping() 事件侦听器来暂停整个队列或连接(而不仅仅是单个作业 class)。与其他方法不同,这将不会在队列暂停时将每个作业置于pop/requeue的循环中,这意味着尝试次数不会增加.

文档是这样说的:

Using the looping method on the Queue facade, you may specify callbacks that execute before the worker attempts to fetch a job from a queue.

https://laravel.com/docs/5.8/queues#job-events

这个 没有 很好地记录的是,如果回调 returns false 那么 the worker will not fetch another job。例如,这将阻止 default 队列从 运行:

Queue::looping(function (\Illuminate\Queue\Events\Looping $event) {
    // $event->connectionName (e.g. "database")
    // $event->queue (e.g. "default")

    if ($event->queue == 'default') {
        return false;
    }
});

注意:事件的 queue 属性 将包含工作进程启动时命令行的值,因此如果您的工作进程正在检查多个队列(例如 artisan queue:work --queue=high,default) 那么事件中 queue 的值将是 'high,default'。作为预防措施,您可能希望用逗号分解字符串并检查 default 是否在列表中。

因此,例如,如果您想创建一个基本的 circuit breaker 来在邮件服务 returns 维护错误时暂停 mail 队列,那么您可以注册一个侦听器,例如这在你的 EventServiceProvider.php:

/**
 * Register any events for your application.
 *
 * @return void
 */
public function boot()
{
    parent::boot();

    Queue::looping(function (\Illuminate\Queue\Events\Looping $event) {
        if (($event->queue == 'mail') && (cache()->get('mail-queue-paused'))) {
            return false;
        }
    });
}

这假设您在应用程序的其他地方有一种机制来检测适当的情况,并且在本例中,该机制需要为共享缓存中的 mail-queue-paused 键分配一个值(因为那是我的代码正在检查什么)。有更强大的解决方案,但在缓存中设置一个特定的众所周知的密钥(并使其自动过期)很简单并且可以达到预期的效果。