Постановка в очередь Guzzle-запросов с ограничениями

Я работаю над приложением Laravel, используя Guzzle 6. Многие функции зависят от API, для которого я создал оболочку.

Моя оболочка представляет собой единственный класс, который создает клиент Guzzle в __construct() и имеет множество общедоступных функций, которые возвращают ответы на запросы Guzzle.

API, который я использую, имеет ограничение в 40 запросов каждые 10 секунд. Я кеширую вещи, поэтому очень редко можно достичь этого предела, но я хотел бы знать, что мое приложение не просто умрет, если это произойдет!

Некоторые заметки о моем приложении:

  • Вызовы API выполняются только в том случае, если такой же вызов не выполнялся в течение последних 6 часов. Если это так, вызов никогда не выполняется, а ответ обслуживается непосредственно из моего кеша Redis.
  • В большинстве случаев вызовы API выполняются с помощью действий пользователя. Само приложение никогда бы не приблизилось к достижению этих пределов.
  • В большинстве случаев у меня уже есть необходимые данные для показа запрошенных страниц пользователям. Вызов API может быть выполнен в фоновом режиме, чтобы узнать, нужно ли что-то обновить с моей стороны, но если у меня уже есть данные, а запрос API не выполнен, это не сделает страницу бесполезной.
  • Приложение работает, https://likethis.tv, если хотите посмотреть. Я использую API TMDb.

Итак, мой вопрос: как мне убедиться, что я не достигну этого предела? Несколько моих идей заключаются в следующем:

  • Используйте систему очередей Laravel, чтобы поместить запросы Guzzle в очередь и обрабатывать их только в том случае, если у нас еще остались запросы. Если нет, подождите, пока не истечет 10-секундное время восстановления...
  • Используйте HandlerStack для Guzzle напрямую. Не уверен, что это возможно, но раньше я использовал HandlerStack для кэширования ответов.

Я пытаюсь не провоцировать слишком самоуверенные ответы, но я уверен, что, вероятно, есть лучший и/или более простой способ, чем вышеописанный, или, если это хорошие идеи, любые указатели или рекомендации были бы замечательными.

Заранее спасибо.


person Daniel Dewhurst    schedule 21.05.2017    source источник
comment
Добавлена ​​награда за это. Я также хотел бы знать, как эффективно регулировать мой собственный вызов API, и я также думал об использовании очередей, но не могу найти правильный способ сделать это.   -  person Jean-Philippe Murray    schedule 25.05.2017
comment
должен ли ответ API передаваться в БД или он должен отображаться клиенту? Второй случай кажется трудным.   -  person cre8    schedule 25.05.2017
comment
Имеет ли значение порядок вызовов API?   -  person Kyslik    schedule 29.05.2017
comment
Ответ используется для обновления/создания записей в базе данных. Порядок не важен. На данный момент моя оболочка обрабатывает только 404, поэтому я думаю, что переделаю этот бит и передам ответ об ошибке тому, что только что сделал вызов, чтобы он мог решить, что делать. Например, мне не нужно выдавать ошибку, если у меня есть запись в моей БД, когда произошла ошибка только в запросе API.   -  person Daniel Dewhurst    schedule 29.05.2017


Ответы (4)


  1. Оберните вызовы API Заданиями и поместите их в отдельную очередь:

    ApiJob::dispatch()->onQueue('api');
    
  2. Используйте пакет mxl/laravel-queue-rate-limit (я автор) для ограничения скорости api очереди. Добавьте это к config/queue.php:

    'rateLimit' => [
        'api' => [
            'allows' => 40,
            'every' => 10
        ]
    ]
    
  3. Запустить обработчик очереди:

    $ php artisan queue:work --queue api
    

См. также этот ответ.

person mixel    schedule 02.08.2019

Недостаточно информации, чтобы по-настоящему разобраться в этом, но для начала хорошие API-интерфейсы обычно возвращают код ответа 429, когда вы превышаете их ограниченный лимит.

Вы можете использовать $res->getStatusCode() из guzzle, чтобы проверить это и вернуть сообщение пользователю, если он делает слишком много запросов слишком быстро.

Можете ли вы предоставить дополнительную информацию о том, что делает ваше приложение? Вы делаете запросы в цикле foreach? Зависит ли представление от данных из этого API?

person Kyle    schedule 25.05.2017
comment
API возвращает полезные коды ошибок, так что это возможно. Я, вероятно, сделаю то, что сказал в комментарии выше к моему вопросу, не буду обрабатывать ответы об ошибках в своей оболочке, а передам их моему контроллеру, поскольку в некоторых случаях API не возвращает то, что мне нужно, - это не конец Мир. - person Daniel Dewhurst; 29.05.2017

Я также работаю над той же проблемой, я предпочел архитектуру на основе обратного вызова, где мой класс Client управляет потоком запросов. В настоящее время я делаю алгоритм сна и проверки. Я работаю для меня, так как у меня есть 3 секунды времени охлаждения.

Я использую Cache для хранения количества запущенных запросов.

while(($count = Cache::get($this->getCacheKey(),0)) >= 40){ // Max request
    sleep(1);
}
Cache::set($this->getCacheKey(), ++$count);
// fire request

function getCacheKey(){
    return floor(time()/10); // Cool down time
}

Очередь кажется лучшим вариантом, и я в конечном итоге перейду к этому. Есть несколько вещей, которые вам нужно иметь в виду, прежде чем ставить очередь между ними.

  1. Архитектура на основе обратного вызова, поскольку вам может потребоваться сохранить состояние сериализации кода в очереди. Дизайн, основанный на обратном вызове, даст полный контроль Client классу. Вам не придется беспокоиться о дросселировании в вашем коде.
  2. Сериализация может быть сложной, попробуйте __sleep и __wakeup.
  3. Вы также можете приоритизировать несколько звонков, вы можете выделить квоту от клиентов для таких звонков.
person anwerj    schedule 30.05.2017

Лично я думаю, что Guzzle не должен обрабатывать этот случай, но если вы хотите, чтобы Guzzle справился с этим, я бы написал промежуточное программное обеспечение, которое проверяет ответ и возвращает ли он ошибку ограничения скорости (например, код состояния 429). Затем вы можете либо выдать пользовательскую ошибку, либо дождаться окончания ограничения скорости и повторить попытку. Однако это может привести к длительному времени отклика (поскольку вы ждете ограничения скорости).

Я не думаю, что очередь Laravel была бы лучше, так как это сделало бы ответ асинхронно доступным, и вам пришлось бы опрашивать вашу базу данных или ваш кеш, где бы вы ни хранили результаты. (Конечно, это может сработать, если вам не нужно, чтобы результаты были доступны немедленно)

Если эта сторонняя служба напрямую подключена к пользовательскому интерфейсу, я, вероятно, применил бы такое же ограничение скорости (в коде вашего приложения) и вернул бы пользователю сообщение об ошибке вместо того, чтобы ждать и решать проблему автоматически.

person mark.sagikazar    schedule 26.05.2017