Как ограничить скорость запросов к веб-сервисам в Python?

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

Я понимаю, что общий сценарий таков: экземпляр класса делает запрос через метод. Когда это происходит, метод генерирует некоторый сигнал, который устанавливает переменную блокировки где-то, и запускает таймер обратного отсчета для количества секунд в limit, (По всей вероятности, блокировка - это сам таймер обратного отсчета.) Если в течение этого временного интервала сделан другой запрос, он должен быть поставлен в очередь, пока таймер обратного отсчета не достигнет нуля и блокировка не будет снята; в этот момент отправляется самый старый запрос в очереди, таймер обратного отсчета сбрасывается, и блокировка вновь включается.

Это случай для потоков? Есть ли другой подход, которого я не вижу?

Должны ли таймер обратного отсчета и блокировка быть переменными экземпляра или они должны принадлежать классу, чтобы все экземпляры класса содержали запросы?

Кроме того, является ли это плохой идеей для предоставления функции ограничения скорости в библиотеке? Я полагаю, поскольку по умолчанию обратный отсчет равен нулю, библиотека все еще позволяет разработчикам использовать библиотеку и предоставлять свои собственные схемы ограничения скорости. Учитывая, что разработчикам, использующим сервис, в любом случае необходимо будет ограничивать скорость запросов, я полагаю, что для библиотеки было бы удобно предоставлять средства ограничения скорости.

Независимо от размещения схемы ограничения скорости в библиотеке или нет, я хочу написать приложение с использованием библиотеки, поэтому предложенные методы пригодятся.

Большое спасибо за ваши предложения!

Крис

7 ответов

Решение

Это лучше работает с очередью и диспетчером.

Вы разделяете свою обработку на две стороны: источник и отправка. Это могут быть отдельные потоки (или отдельные процессы, если это проще).

Сторона источника создает и ставит в очередь запросы с любой скоростью, которая делает их счастливыми.

Диспетчерская сторона делает это.

  1. Получить время начала запроса, с.

  2. Отменяет запрос, обрабатывает запрос через удаленный сервис.

  3. Получить текущее время, т. Сон для скорости - (т - с) секунд.

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

Сложной частью этого является создание некоторого представления для каждого запроса, который вы можете поставить в очередь. Поскольку Python Queue будет обрабатывать практически все, вам не нужно много делать.

Если вы используете многопроцессорную обработку, вам придется мариновать свои объекты, чтобы поместить их в трубу.

Не изобретайте велосипед, если это не требуется. Проверьте удивительный предел скорости библиотеки. Идеально, если вы просто хотите по каким-либо причинам ограничить количество звонков на api отдыха и продолжить свою жизнь.

В документации пакета yfinance показан хороший и лаконичный способ одновременного ограничения скорости и кэширования ответов. Как и во время разработки и отладки, мне часто приходится выполнять одни и те же запросы снова и снова.

      from requests import Session
from requests_cache import CacheMixin, SQLiteCache
from requests_ratelimiter import LimiterMixin, MemoryQueueBucket
from pyrate_limiter import Duration, RequestRate, Limiter

class CachedLimiterSession(CacheMixin, LimiterMixin, Session)

session = CachedLimiterSession(
    limiter=Limiter(RequestRate(2, Duration.SECOND*5)),  # max 2 requests per 5 seconds
    bucket_class=MemoryQueueBucket,
    backend=SQLiteCache("yfinance.cache"),
)
response = requests.get('https://httpbin.org/get')
response.raise_for_status()
response.json()

Очереди могут быть слишком сложными. Более простое решение - дать вашему классу переменную для времени последнего вызова сервиса. Когда служба вызывается (!1), установите для waitTime значение delay - Now + lastcalltime, delay должно быть равно минимально допустимому времени между запросами. Если это число положительное, поспите долго перед тем, как позвонить (!2). Недостаток / преимущество этого подхода в том, что он обрабатывает запросы веб-службы как синхронные. Преимущество в том, что это нелепо просто и легко реализуемо.

  • (! 1): должно произойти сразу после получения ответа от службы внутри оболочки (возможно, в нижней части оболочки).
  • (! 2): должно происходить, когда вызывается оболочка python вокруг веб-службы, в верхней части оболочки.

Конечно, решение С.Лотта более элегантно.

Так что я предполагаю, что что-то простое, например, время импорта time.sleep(2) не будет работать для ожидания 2 секунд между запросами

Ваша схема ограничения скорости должна находиться под сильным влиянием соглашений о вызовах базового кода (синхронного или асинхронного), а также того, в какой области (поток, процесс, машина, кластер?) Будет работать это ограничение скорости.

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

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

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

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

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

Другие вопросы по тегам