2017-06-26 6 views
0

J'essaie de créer une API qui va collecter les réponses de plusieurs autres API et combiner les résultats en une seule réponse. Je veux envoyer les requêtes get de manière asynchrone afin qu'elle s'exécute plus rapidement, mais même si j'utilise des coroutines et que je cède, mon code semble toujours faire une requête à la fois. Vous vous demandez peut-être si c'est parce que j'utilise la bibliothèque de requêtes au lieu de AsyncHTTPClient de tornado, ou parce que j'appelle self.path_get dans une boucle, ou parce que je stocke les résultats dans une variable d'instance?Frapper plusieurs API à la fois, tornade et python

Les API que je suis en train de retourner des tableaux d'objets JSON, et je veux les combiner tous dans un tableau et écrire cela à la réponse.

from tornado import gen, ioloop, web 
from tornado.gen import Return 
import requests 


PATHS = [ 
    "http://firsturl", 
    "http://secondurl", 
    "http://thirdurl" 
] 


class MyApi(web.RequestHandler): 

    @gen.coroutine 
    def get(self): 
     self.results = [] 
     for path in PATHS: 
      x = yield self.path_get(path) 

     self.write({ 
      "results": self.results, 
     }) 

    @gen.coroutine 
    def path_get(self, path): 
     resp = yield requests.get(path) 
     self.results += resp.json()["results"] 
     raise Return(resp) 


ROUTES = [ 
    (r"/search", MyApi), 
] 


def run(): 
    app = web.Application(
     ROUTES, 
     debug=True, 
    ) 

    app.listen(8000) 

    ioloop.IOLoop.current().start() 


if __name__ == "__main__": 
    run() 

Répondre

2

Il existe de nombreuses raisons pour lesquelles votre code ne fonctionne pas. Pour commencer, requests bloque généralement la boucle d'événements et ne laisse rien d'autre s'exécuter. Remplacez requests par AsyncHTTPClient.fetch. De plus, la façon dont vous donniez chaque requête rendait les requêtes séquentielles et non simultanées comme vous le pensiez. Voici un exemple de la façon dont votre code pourrait être restructuré:

import json 
from tornado import gen, httpclient, ioloop, web 

# ... 

class MyApi(web.RequestHandler): 

    @gen.coroutine 
    def get(self): 
     futures_list = [] 
     for path in PATHS: 
      futures_list.append(self.path_get(path)) 

     yield futures_list 
     result = json.dumps({'results': [x.result() for x in futures_list]}) 
     self.write(result) 

    @gen.coroutine 
    def path_get(self, path): 
     request = httpclient.AsyncHTTPClient() 
     resp = yield request.fetch(path) 
     result = json.loads(resp.body.decode('utf-8')) 
     raise gen.Return(result) 

Ce qui se passe est que nous créons une liste de Futures qui se est revenu de gen.coroutine fonctions et qui donne toute la liste jusqu'à ce que les résultats de la demande sont disponibles. Ensuite, une fois toutes les requêtes terminées, futures_list est itéré et les résultats sont utilisés pour créer une nouvelle liste qui est ajoutée à un objet JSON.

+0

Oui, ça marche, merci! – littlegreenbird