【问题标题】:How to run only one job at time in same laravel queue?如何在同一个 laravel 队列中一次只运行一项作业?
【发布时间】:2020-04-21 21:37:33
【问题描述】:

我有使用 API 的 saas 服务。它有限制,所以我需要一个用户帐户同时只执行一个请求。

为此我与OnQueue($user->name);排队

然后在handle()做工作...

我只需要一个作业可以在用户队列中运行。同时可能只运行不同的队列每 1 个队列 1 个作业。

我正在使用 redis 连接。

这是我的工作课程:

 public function __construct(Accounts $acc)
    {
        $this->acc = $acc;
        $this->ownjob = $acc->prepareJobQueue();
    }

    public function handle()
    {
        $acc = $this->acc;
        $job = $this->ownjob;

        $api = new Api([
            'login'     => $acc->login,
            'password'  => $acc->password,
        ]);
        if ($api->checkLogin()) {
            info("{$acc->login} OK Authorized");
            foreach ($job['queue'] as $term) {
                switch($term['type']) {
                    case 'hashtag':
                        info("{$acc->login} Queuing: type - {$term['type']}, value - {$term['value']}");
                        $hashtag = Hashtags::where('cha_name',$term['value'])->first();
                        $answer = $api->getUsersByHashtag($hashtag,50);
                        break;
                    case 'concurency':
                        info("{$acc->login} Queuing: type - {$term['type']}, value - {$term['value']}");
                        $soc_user = Users::where('soc_unique_id',$term['value'])->first();
                        $answer = $api->getUserFollowers($soc_user);
                        break;
                    default:
                        break;
                }
            }
        } else {
            info("{$acc->login} NOT Authorized - STOP JOB");
        }

    }

这就是我派遣工作的方式:

$accounts = Accounts::select(['id', 'login', 'hashtag_filter', 'concurency_filter'])->whereNotNull('hashtag_filter')->get();
        foreach ($accounts as $acc) {
            doFollowing::dispatch($acc)->onQueue($acc->login);
        }

【问题讨论】:

    标签: laravel queue


    【解决方案1】:

    您可以在 Supervisor 或 Horizo​​n 设置中限制每个队列的 numprocs

    如果你只为每个用户生成一个队列工作者,我相信你会得到你想要的行为。

    【讨论】:

    • 用户数约为 100 时会很好用,但如果用户数为 10k 怎么办?也正如我所说 - 队列名称是用户名。在您的变体中,我需要始终修改主管配置?
    【解决方案2】:

    使用mxl/laravel-queue-rate-limit Composer 包。

    它使您能够在不使用第三方驱动程序(例如 Redis)的情况下对特定队列上的 Laravel 作业进行速率限制。

    1. 安装它:

      $ composer require mxl/laravel-queue-rate-limit:^1.0
      
    2. 此包与 Laravel 5.5+ 兼容,并使用 [auto-discovery][1] 功能将 MichaelLedin\LaravelQueueRateLimit\QueueServiceProvider::class 添加到提供程序。

    3. config/queue.php 添加速率限制(x 每秒作业数)设置:

      'rateLimit' => [
          'mail' => [
              'allows' => 1, // number of jobs
              'every' => 5 // time interval in seconds
          ]
      ]
      

      这些设置允许在 mail 队列上每 5 秒运行 1 个作业。 确保默认队列驱动程序(config/queue.php 中的default 属性)设置为除sync 之外的任何值。

    4. 使用--queue mail 选项运行队列工作者:

      $ php artisan queue:work --queue mail
      

      您可以在多个队列上运行工作器,但只有 rateLimit 设置中引用的队列会受到速率限制:

      $ php artisan queue:work --queue mail,default
      

      default 队列上的作业将在没有速率限制的情况下执行。

    5. 排队一些作业以测试速率限制:

      SomeJob::dispatch()->onQueue('mail');
      SomeJob::dispatch()->onQueue('mail');
      SomeJob::dispatch()->onQueue('mail');
      SomeJob::dispatch();
      

    【讨论】:

    • 我有动态队列命名,因为它是每个用户的队列 - 我不能总是为每个用户修改配置。另外我问的不是每次限制,我想同时在同一个队列中只运行一项作业 - 不管是每分钟 1 次还是 100 次
    【解决方案3】:

    您可以为此使用 Laravel 的内置速率限制(请注意,它确实需要 Redis)。

    在你的工作中:

    Redis::funnel('process-name')->limit(1)->then(function () {
        // Your job logic here
    });
    

    请注意,如果您不提供第二次回调 then ,如果它无法获得锁,则会抛出异常(这将导致您的工作失败)

    如果您使用的是 Laravel 6 或更高版本,您也可以选择在作业中间件中执行此操作,而不是在作业本身中(如果您有多个共享同一个锁的作业很方便)

    更多关于 Laravel 限速的信息:https://laravel.com/docs/5.8/queues#rate-limiting

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-09
      • 2017-11-20
      • 2019-11-24
      • 2017-02-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多