Запросы на подачу в 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 для ответов кэширования.

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

Спасибо заранее.

Ответы

Ответ 1

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

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

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

Ответ 2

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

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

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

Ответ 3

Я также работаю над одной и той же проблемой, я предпочел основанную на обратном вызове архитектуру, где мой класс 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
}

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

  • Архитектура, основанная на обратном вызове, потому что вам может потребоваться сохранить сериализованное состояние кода в очереди. Конструкция с обратным вызовом даст управление всем элементам управления Client Class. Вам не придется беспокоиться о дросселировании в вашем коде.
  • Сериализация может быть сложной, попробуйте __sleep и __wakeup.
  • Вы также можете указать приоритет на несколько вызовов, вы можете выделить квоту от клиентов для таких вызовов.