Как замедлить асинхронные вызовы API, чтобы соответствовать ограничениям API? - PullRequest
0 голосов
/ 24 августа 2018

У меня есть список ~ 300K URL-адресов для API, с которого мне нужно получать данные.

Предел API составляет 100 вызовов в секунду.

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

Как замедлить асинхронность, чтобы я мог совершать 100 вызовов в секунду?

import grequests

lst = ['url.com','url2.com']

class Test:
    def __init__(self):
        self.urls = lst

    def exception(self, request, exception):
        print ("Problem: {}: {}".format(request.url, exception))

    def async(self):
        return grequests.map((grequests.get(u) for u in self.urls), exception_handler=self.exception, size=5)



    def collate_responses(self, results):
        return [x.text for x in results]

test = Test()
#here we collect the results returned by the async function
results = test.async()
response_text = test.collate_responses(results)

Ответы [ 3 ]

0 голосов
/ 24 августа 2018

Вы можете просто отслеживать, сколько времени прошло, и решать, хотите ли вы делать больше запросов или нет.

Это напечатает 100 чисел в секунду, например:

from datetime import datetime
import time

start = datetime.now()
time.sleep(1);
counter = 0
while (True):
    end = datetime.now()
    s = (end-start).seconds
    if (counter >= 100):
        if (s <= 1):
            time.sleep(1) # You can keep track of the time and sleep less, actually
            start = datetime.now()
            counter = 0
    print(counter)
    counter += 1
0 голосов
/ 02 сентября 2018

Первым шагом, который я предпринял, было создание объекта, который может распределять максимум n монет каждые t мс.

import time

class CoinsDistribution:
    """Object that distribute a maximum of maxCoins every timeLimit ms"""
    def __init__(self, maxCoins, timeLimit):
        self.maxCoins = maxCoins
        self.timeLimit = timeLimit
        self.coin = maxCoins
        self.time = time.perf_counter()


    def getCoin(self):
        if self.coin <= 0 and not self.restock():
            return False

        self.coin -= 1
        return True

    def restock(self):
        t = time.perf_counter()
        if (t - self.time) * 1000 < self.timeLimit:
            return False
        self.coin = self.maxCoins
        self.time = t
        return True

Теперь нам нужен способ заставить функцию вызываться только если они могутполучить монетуДля этого мы можем написать функцию декоратора, которую мы могли бы использовать следующим образом:

@limitCalls(callLimit=1, timeLimit=1000)
def uniqFunctionRequestingServer1():
    return 'response from s1'

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

server_2_limit = CoinsDistribution(3, 1000)

@limitCalls(server_2_limit)
def sendRequestToServer2():
    return 'it worked !!'

@limitCalls(server_2_limit)
def sendAnOtherRequestToServer2():
    return 'it worked too !!'

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

import functools

def limitCalls(obj=None, *, callLimit=100, timeLimit=1000):
    if obj is None:
        obj = CoinsDistribution(callLimit, timeLimit)

    def limit_decorator(func):
        @functools.wraps(func)
        def limit_wrapper(*args, **kwargs):
            if obj.getCoin():
                return func(*args, **kwargs)
            return 'limit reached, please wait'
        return limit_wrapper
    return limit_decorator

И готово!Теперь вы можете ограничить количество вызовов любого API, который вы используете, и вы можете создать словарь для отслеживания ваших объектов CoinsDistribution, если вам приходится управлять многими из них (для разных конечных точек API или для разных API).

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

0 голосов
/ 24 августа 2018

Этот другой вопрос в SO показывает, как именно это сделать. Кстати, то, что вам нужно, обычно называется throttling .

...