Очередь Laravel Redis игнорирует регулирование

Я использую Laravel Horizon и Redis, и я пытаюсь ограничить его. Я использую внешний API с ограничением скорости 100 запросов в минуту. Мне нужно сделать около 700 запросов. У меня он настроен так, что каждое задание, которое я добавляю в очередь, выполняет только один вызов API в самом задании. Так что, если я задушу очередь, я смогу оставаться в рамках. По какой-то причине дросселирования не происходит, и вместо этого происходит продувка очереди (что, конечно, вызывает множество ошибок API). Однако дроссель работает локально, но не на моем сервере.

Изначально я пытался задросселировать документацию очереди Laravel, но смог заставить ее работать только локально, поэтому я переключился на опробование laravel-queue-rate-limit на Github. В соответствии с README я добавил в свой queue.php файл конфигурации следующее:

'rateLimits' => [
        'default' => [ // queue name
            'allows' => 75, // 75 job
            'every' => 60 // per 60 seconds
        ]
    ],     

По какой-то причине регулирование работает правильно, когда я запускаю его в своей локальной среде Ubuntu, но он не работает на моем сервере (также в Ubuntu). На сервере он просто продувает очередь, как будто дроссельной заслонки нет.

Есть ли что-то, что я делаю неправильно, или, может быть, лучший способ справиться с внешним API с ограничением скорости?

Изменить 1:

config / horizon.php

    'environments' => [
        'production' => [
            'supervisor-1' => [
                'connection' => 'redis',
                'queue' => ['default'],
                'balance' => 'simple',
                'processes' => 3,
                'tries' => 100,
            ],
        ],

Одна из ручек, с которой начинается большинство работ:

    public function handle()
    {
        $updatedPlayerIds = [];
        foreach ($this->players as $player) {
            $playerUpdate = Player::updateOrCreate(
                [
                    'id' => $player['id'],
                ],
                [
                    'faction_id' => $player['faction_id'],
                    'name' => $player['name'],
                ]
            );

            // Storing id's of records updated
            $updatedPlayerIds[] = $playerUpdate->id;

            // If it's a new player or the player was last updated awhile ago, then get new data!
            if ($playerUpdate->wasRecentlyCreated ||
                $playerUpdate->last_complete_update_at == null ||
                Carbon::parse($playerUpdate->last_complete_update_at)->diffInHours(Carbon::now()) >= 6) {
                Log::info("Updating '{$playerUpdate->name}' with new data", ['playerUpdate' => $playerUpdate]);
                UpdatePlayer::dispatch($playerUpdate);
            } else {
//                Log::debug("Player data fresh, no update performed", ['playerUpdate' => $playerUpdate]);
            }
        }

        //Delete any ID's that were not updated via the API
        Player::where('faction_id', $this->faction->id)->whereNotIn('id', $updatedPlayerIds)->delete();
    }

Кроме того, вот приблизительная диаграмма, которую я сделал, пытаясь проиллюстрировать, как у меня есть несколько файлов PHP заданий, которые в конечном итоге создаются за короткий промежуток времени, особенно такие, как updatePlayer(), которые часто создаются 700 раз.

диаграмма


person ComputerLocus    schedule 28.06.2020    source источник
comment
Не могли бы вы попробовать добавить еще одну пару "ключ-значение" в массив default - block => 60? Возможно, это проблема тайм-аута, когда тайм-аут срабатывает через 3 секунды и сразу же вызывается другой обратный вызов.   -  person Qumber    schedule 30.06.2020
comment
Кроме того, похоже, что вышеупомянутый пакет не работает с Horizon. Вам, вероятно, будет лучше с очередью Laravel. В очереди Laravel добавьте это в Redis::throttle('key' ) - ->block(60).   -  person Qumber    schedule 30.06.2020
comment
@Qumber Добавление блокировки похоже на исправление! Я также переключился на обычное регулирование Laravel, о котором вы упомянули. В любом случае определенно предпочитаю использовать встроенные методы.   -  person ComputerLocus    schedule 01.07.2020
comment
@Qumber Однако теперь проблема, кажется, в том, что они часто просто сидят в очереди довольно долгое время. Я добавил промежуточное ПО, которое применяет этот дроссель: Redis::throttle('torn-api')->allow(75)->every(60)->block(60). Я пытаюсь разрешить выполнение только 75 заданий за 60 секунд.   -  person ComputerLocus    schedule 01.07.2020
comment
@Qumber На самом деле они, кажется, просто застревают там, и если я вручную попытаюсь очистить очередь, используя php artisan queue:work, а не через superviserd worker, я получаю ошибку Illuminate\Queue\MaxAttemptsExceededException: App\Jobs\UpdatePlayer has been attempted too many times or run too long. The job may have previously timed out. in. И я увижу кучу попыток на них вроде 20 попыток.   -  person ComputerLocus    schedule 01.07.2020
comment
Не могли бы вы вставить в вопрос всю функцию ручки? Кроме того, определили ли вы тайм-аут и максимальное количество повторов в конфигурации Horizon (config/horizon.php)? Возможно, сделайте это и зарегистрируйте любое исключение, используя метод failed в самом задании; вот так - stackoverflow.com/a/53172981/10625611   -  person Qumber    schedule 01.07.2020
comment
@Qumber выполняет некоторые из упомянутых вами вещей, а затем запускает очередь прямо из командной строки через php artisan queue:work, кажется намного более надежным, чем метод супервизора. Я рассматриваю просто фоновый процесс очереди вместо супервизора, поскольку он кажется слишком непоследовательным. Также я отредактировал некоторые дополнительные детали в OP   -  person ComputerLocus    schedule 03.07.2020
comment
Для меня это просто похоже на проблему с конфигурацией - укажите значения retry_after и timeout в конфигурации Horizon. Ваше retry_after значение в конфигурации всегда должно быть больше, чем время, необходимое для выполнения работы. И значение timeout должно быть на несколько секунд короче, чем значение retry_after. См. Эту проблему и этот пункт в doc   -  person Qumber    schedule 03.07.2020
comment
@Qumber Отлично, я думаю, что теперь это исправлено. Поскольку я предполагаю, что вы снова захотите получить эту награду, я расскажу, что я в конечном итоге изменил. queue.php retry_after установлен на 70 В Horizon.php waits.redis:default изменен на 65, а timeout изменен на 60 (в разделе сред). Я также удалил различные флаги, которые у меня были на слушателях очереди супервизора, чтобы использовались только значения конфигурации. Также в моем промежуточном программном обеспечении RateLimited я добавил ->block(60), как вы упомянули. И я вернулся к обычному троттлингу вместо использования пакета.   -  person ComputerLocus    schedule 05.07.2020
comment
Рад помочь. :). Я определенно ищу награду. : D Преобразование в ответ.   -  person Qumber    schedule 05.07.2020


Ответы (1)


Похоже, упомянутый вами пакет (laravel-queue-rate-limit) не работает с Horizon. Вам, вероятно, лучше использовать встроенный метод Laravel.
В очереди Laravel добавьте ->block(60) для соответствия ->every(60), чтобы тайм-аут по умолчанию не сработал и не вызвал другой обратный вызов до 60 секунд.

Redis::throttle('torn-api')->allow(75)->every(60)->block(60)

Также рассмотрите возможность добавления значений тайм-аута и максимального числа повторов в конфигурацию Horizon (config/horizon.php). Вы также можете зарегистрировать любое исключение, используя метод failed в самом задании. См. это.

Задайте значения retry_after и timeout в конфигурации Horizon. Ваше значение retry_after в конфигурации всегда должно быть больше, чем время, необходимое для выполнения работы. И значение timeout должно быть на несколько секунд короче, чем значение retry_after. Это гарантирует, что работник, обрабатывающий заданное задание, всегда будет убит перед повторной попыткой задания. См. Эту проблему и эту укажите в документации.

person Qumber    schedule 05.07.2020