2017-06-25 3 views
1

Je suis en train de développer un bot avec le module pyTelegramBotAPI, qui fonctionne via des webhooks avec Flask + Gunicorn installé comme serveur pour les webhooks. Gunicorn travaille avec 5 travailleurs pour une meilleure vitesse, la structure de mon projet ressemble à ça:Limite de débit de l'API pour le télégramme bot

app.py 
bot.py 

En bot.py j'ai une fonction pour les mises à jour de traitement:

def process_the_update(update): 
    logger.info(update) 
    update = types.Update.de_json(update) 
    bot.process_new_updates([update]) 

Dans app.py i importé cette fonction, donc, quand une mise à jour arrive, app.py appellera cette fonction, et bot gérera la mise à jour. Dans mon bot, l'utilisateur peut appeler une commande, qui utilisera une API externe pour obtenir des informations. Le problème est que cette API externe a une limite de 3 requêtes par seconde. J'ai besoin de configurer un bot avec une telle limite de débit. Tout d'abord je pensais que de le faire avec la file d'attente avec le code comme ceci:

lock_queue = Queue(1) 
requests_queue = Queue(3) 
def api_request(argument): 
    if lock_queue.empty(): 
     try: 
      requests_queue.put_nowait(time.time()) 
     except queue.Full: 
      lock_queue.put(1) 
      first_request_time = requests_queue.get() 
      logger.info('First request time: ' + str(first_request_time)) 
      current_time = time.time() 
      passed_time = current_time - first_request_time 
      if passed_time >= 1: 
       requests_queue.put_nowait(time.time()) 
       lock_queue.get() 
      else: 
       logger.info(passed_time) 
       time.sleep(1 - passed_time) 
       requests_queue.put_nowait(time.time()) 
       lock_queue.get() 
    else: 
     lock_queue.put(1) 
     first_request_time = vk_requests_queue.get() 
     logger.info('First request time: ' + str(first_request_time)) 
     current_time = time.time() 
     passed_time = current_time - first_request_time 
     if passed_time >= 1: 
      requests_queue.put_nowait(time.time()) 
      lock_queue.get() 
     else: 
      logger.info(passed_time) 
      time.sleep(1 - passed_time) 
      requests_queue.put_nowait(time.time()) 
      lock_queue.get() 
    result = make_api_request(argument) # requests are made too by external module. 
    return result 

La logique était que, comme je pensais, parce que le module pyTelegramBotAPI utilise la manipulation des fils pour accélérer les mises à jour, toutes les discussions vérifieraient requests_queue, qui aura le temps de 3 dernières api_requests, et ainsi l'heure de la première des 3 requêtes effectuées sera comparée à l'heure actuelle (à vérifier, si une seconde est passée). Et, parce que je devais être sûr, qu'un seul thread ferait ce genre de comparaison simultanément, j'ai fait lock_queue. Mais, le problème est que, d'abord, gunicorn utilise 5 travailleurs, donc il y aura toujours la possibilité, que tous les messages des utilisateurs seront traités dans des processus différents, et ces processus auront leurs propres files d'attente. Et, deuxièmement, même si j'ai mis le nombre de travailleurs par défaut (1 travailleur), je reçois toujours 429 erreur, donc je pense, que mon code ne fonctionnera pas comme je le voulais du tout. Je pensais faire une limite de taux avec redis, donc chaque fois dans chaque thread et processus bot va vérifier l'heure des 3 dernières demandes, mais je ne suis pas sûr, que c'est la bonne façon, et je ne suis pas sûr , comment écrire ceci.

serais heureux, si quelqu'un suggère des idées ou même des exemples de code (api externe ne fournit pas de tête limite x taux)

Répondre

0

A écrit cette fonction, en utilisant Redis compter les demandes (sur la base de ce https://www.binpress.com/tutorial/introduction-to-rate-limiting-with-redis/155 tutoriel)

import redis 

r_db = redis.Redis(port=port, db=db) 

def limit_request(request_to_make, limit=3, per=1, request_name='test', **kwargs): 
    over_limit_lua_ = ''' 
    local key_name = KEYS[1] 
    local limit = tonumber(ARGV[1]) 
    local duration = ARGV[2] 

    local key = key_name .. '_num_of_requests' 
    local count = redis.call('INCR', key) 
    if tonumber(count) > limit then 
     local time_left = redis.call('PTTL', key) 
     return time_left 
    end 
    redis.call('EXPIRE', key, duration) 
    return -2 
    ''' 

    if not hasattr(r_db, 'over_limit_lua'): 
     r_db.over_limit_lua = r_db.register_script(over_limit_lua_) 

    request_possibility = int(r_db.over_limit_lua(keys=request_name, args=[limit, per])) 
    if request_possibility > 0: 
     time.sleep(request_possibility/1000.0) 
     return limit_request(request_to_make, limit, per, request_name, **kwargs) 
    else: 
     request_result = request_to_make(**kwargs) 
     return request_result