2013-01-29 8 views
3

J'implémente un service Web SOAP en utilisant tornado (et le module tiers tornadows). L'une des opérations dans mon service doit appeler un autre, donc j'ai la chaîne:Appels de service Web imbriqués avec tornade (async?)

  1. demande externe (via l'SoapUI) à l'opération A
  2. demande interne (via le module de requêtes) pour le fonctionnement B
  3. réponse interne du fonctionnement B
  4. de réponse externe de l'opération A

Parce qu'il est en cours d'exécution tout en un seul service, il est bloqué quelque part bien. Je ne suis pas familier avec la fonctionnalité asynchrone de tornado.

Il existe une seule méthode de traitement des requêtes (post) car tout arrive sur l'URL unique, puis l'opération spécifique (méthode de traitement) est appelée en fonction de la valeur de l'en-tête de requête SOAPAction. J'ai décoré ma méthode post avec @ tornado.web.asynchronous et appelé self.finish() à la fin mais pas de dés.

Peut tornado gérer ce scénario et si oui, comment puis-je l'implémenter?

EDIT (code ajouté):

class SoapHandler(tornado.web.RequestHandler): 
    @tornado.web.asynchronous 
    def post(self): 
     """ Method post() to process of requests and responses SOAP messages """ 
     try: 
      self._request = self._parseSoap(self.request.body) 
      soapaction = self.request.headers['SOAPAction'].replace('"','') 
      self.set_header('Content-Type','text/xml') 
      for operations in dir(self): 
       operation = getattr(self,operations) 
       method = '' 
       if callable(operation) and hasattr(operation,'_is_operation'): 
        num_methods = self._countOperations() 
        if hasattr(operation,'_operation') and soapaction.endswith(getattr(operation,'_operation')) and num_methods > 1: 
         method = getattr(operation,'_operation') 
         self._response = self._executeOperation(operation,method=method) 
         break 
        elif num_methods == 1: 
         self._response = self._executeOperation(operation,method='') 
         break 
      soapmsg = self._response.getSoap().toprettyxml() 
      self.write(soapmsg) 
      self.finish() 
     except Exception as detail: 
      #traceback.print_exc(file=sys.stdout) 
      wsdl_nameservice = self.request.uri.replace('/','').replace('?wsdl','').replace('?WSDL','') 
      fault = soapfault('Error in web service : {fault}'.format(fault=detail), wsdl_nameservice) 
      self.write(fault.getSoap().toxml()) 
      self.finish() 

Ceci est la méthode de poste à partir du gestionnaire de requêtes. C'est à partir du module de services web que j'utilise (donc pas mon code) mais j'ai ajouté le décorateur asynchrone et self.finish(). Tout ce qu'il fait est d'appeler l'opération correcte (comme dictée dans le SOAPAction de la demande).

class CountryService(soaphandler.SoapHandler): 
    @webservice(_params=GetCurrencyRequest, _returns=GetCurrencyResponse) 
    def get_currency(self, input): 
     result = db_query(input.country, 'currency') 
     get_currency_response = GetCurrencyResponse() 
     get_currency_response.currency = result 
     headers = None 
     return headers, get_currency_response 

    @webservice(_params=GetTempRequest, _returns=GetTempResponse) 
    def get_temp(self, input): 
     get_temp_response = GetTempResponse() 
     curr = self.make_curr_request(input.country) 
     get_temp_response.temp = curr 
     headers = None 
     return headers, get_temp_response 

    def make_curr_request(self, country): 

     soap_request = """<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:coun='CountryService'> 
    <soapenv:Header/> 
    <soapenv:Body> 
     <coun:GetCurrencyRequestget_currency> 
     <country>{0}</country> 
     </coun:GetCurrencyRequestget_currency> 
    </soapenv:Body> 
</soapenv:Envelope>""".format(country) 

     headers = {'Content-Type': 'text/xml;charset=UTF-8', 'SOAPAction': '"http://localhost:8080/CountryService/get_currency"'} 
     r = requests.post('http://localhost:8080/CountryService', data=soap_request, headers=headers) 
     try: 
      tree = etree.fromstring(r.content) 
      currency = tree.xpath('//currency') 
      message = currency[0].text 
     except: 
      message = "Failure" 
     return message 

Ce sont deux des opérations du service Web (get_currency & get_temp). Donc SOAPUI frappe get_temp, ce qui fait une requête SOAP à get_currency (via make_curr_request et le module de requêtes). Ensuite, les résultats devraient simplement revenir en arrière et être renvoyés à SOAPUI.

Le fonctionnement réel du service n'a aucun sens (retourner la monnaie lorsqu'on lui demande la température), mais j'essaie simplement de faire fonctionner la fonctionnalité et ce sont les opérations que j'ai.

+0

Avez-vous essayé d'utiliser '@ tornado.web.asynchronous'? http://www.tornadoweb.org/documentation/web.html?highlight=async#tornado.web.asynchronous Veuillez poster du code si cela ne fonctionne pas, et je serai heureux de vous aider à résoudre votre problème. –

+0

@StevePeak merci pour votre aide. Je l'ai utilisé pour la méthode post, mais je ne sais pas si je le fais correctement. Code pertinent ajouté maintenant. – johnharris85

+0

Je vois quelques problèmes de syntaxe dans votre demande. Quelles sont les 'exceptions 'que vous voyez sur ce service (par exemple' NameError')? –

Répondre

6

Je ne pense pas que votre module de savon, ou les demandes sont asynchrones. Je crois que l'ajout du décorateur @asyncronous n'est que la moitié de la bataille. Pour l'instant, vous ne faites aucune requête asynchrone à l'intérieur de votre fonction (chaque requête bloque le serveur jusqu'à ce que votre méthode se termine)

Vous pouvez le faire en utilisant les tornados AsynHttpClient. Cela peut être utilisé comme un remplacement exact des demandes. De l'docoumentation exemple:

class MainHandler(tornado.web.RequestHandler): 
    @tornado.web.asynchronous 
    def get(self): 
     http = tornado.httpclient.AsyncHTTPClient() 
     http.fetch("http://friendfeed-api.com/v2/feed/bret", 
        callback=self.on_response) 

    def on_response(self, response): 
     if response.error: raise tornado.web.HTTPError(500) 
     json = tornado.escape.json_decode(response.body) 
     self.write("Fetched " + str(len(json["entries"])) + " entries " 
        "from the FriendFeed API") 
     self.finish() 

Leur méthode est décorée avec async et ils font des demandes asyn http. C'est là que le flux devient un peu étrange. Lorsque vous utilisez AsyncHttpClient, il ne bloque pas la boucle d'événements (SVP, je viens de commencer à utiliser tornado cette semaine, soyez tranquille si toute ma terminologie n'est pas encore correcte). Cela permet au serveur de traiter librement les demandes entrantes. Lorsque votre requête asynchttp est terminée, la méthode de rappel sera exécutée, dans ce cas on_response.

Ici, vous pouvez facilement remplacer les demandes avec le client tornado asynchttp. Pour votre service de savon, cependant, les choses pourraient être plus compliquées. Vous pourriez faire une webserivce locale autour de votre client de savon et lui faire des demandes asynchrones en utilisant le client HTTP de tornado asyn ???

Cela va créer une logique de rappel complexe qui peut être fixé à l'aide de la gen decorator

+0

Salut @ dm03514, pouvez-vous me dire comment pouvons-nous envoyer demande le corps avec lui, en fait j'essaie d'appeler api savon async en utilisant de la mousse, mais ne recevant aucune réponse de celui-ci, voici ma question: http://stackoverflow.com/questions/39227719/asynchronous-soap-api-call-using -python, s'il vous plaît regardez dans ceci, peut être vous pouvez m'aider –

0

Ce problème a été résolu depuis hier.

demande Pull: https://github.com/rancavil/tornado-webservices/pull/23

Exemple: ici d'un simple webservice qui ne prend pas d'arguments et renvoie la version. Remarquez, vous devez:

  • déclaration de la méthode: décorer la méthode avec @gen.coroutine
  • résultats retour: utilisation raise gen.Return(data)

code:

from tornado import gen 
from tornadows.soaphandler import SoapHandler 
... 

class Example(SoapHandler): 
    @gen.coroutine 
    @webservice(_params=None, _returns=Version) 
    def Version(self): 
     _version = Version() 
     # async stuff here, let's suppose you ask other rest service or resource for the version details. 
     # ... 
     # returns the result. 
     raise gen.Return(_version) 

Cheers!

Questions connexes