龙卷风嵌套的Web服务调用(异步?)

时间:2013-01-29 15:10:17

标签: python web-services asynchronous tornado

我正在使用tornado(以及第三方tornadows模块)实现SOAP Web服务。我服务中的一个操作需要调用另一个操作,所以我有链:

  1. (通过SOAPUI)对操作A的外部请求
  2. 内部请求(通过请求模块)进入操作B
  3. B行动的内部回应
  4. 来自A行动的外部回应
  5. 因为它全部在一个服务中运行,所以它在某处被阻止了。我不熟悉龙卷风的异步功能。

    只有一个请求处理方法(post),因为所有内容都在单个url上,然后根据SOAPAction请求头值调用特定操作(处理方法)。我用@ tornado.web.asynchronous修改了post方法,最后调用了self.finish()但没有骰子。

    龙卷风可以处理这种情况,如果可以,我该如何实施呢?

    编辑(添加代码):

    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()
    

    这是来自请求处理程序的post方法。它来自我正在使用的Web服务模块(所以不是我的代码),但我添加了异步装饰器和self.finish()。所有它基本上都是调用正确的操作(如请求的SOAPAction中所指示的)。

    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
    

    这是Web服务的两个操作(get_currency&amp; get_temp)。因此,SOAPUI命中get_temp,它向get_currency发出SOAP请求(通过make_curr_request和requests模块)。然后结果应该链回来并发送回SOAPUI。

    服务的实际操作毫无意义(当被要求提供温度时返回货币),但我只是想让功能正常运行,这些都是我的操作。

2 个答案:

答案 0 :(得分:6)

我认为您的肥皂模块或请求不是异步的。

我相信添加@asyncronous装饰只是成功的一半。现在你没有在你的函数内部发出任何异步请求(每个请求都是阻塞的,这会占用服务器直到你的方法完成)

您可以使用tornados AsynHttpClient进行切换。这几乎可以用作请求的确切替代品。来自docoumentation示例:

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()

他们的方法用异步装饰,他们正在制作asyn http请求。这是流程有点奇怪的地方。当您使用AsyncHttpClient时,它不会锁定事件循环(本周刚刚开始使用龙卷风的PLease,如果我的所有术语都不正确,请放轻松)。这允许服务器自由处理传入的请求。当您的异步请求完成后,将执行回调方法,在本例中为on_response

在这里,您可以轻松地使用tornado asynchttp客户端替换请求。但是,对于你的肥皂服务,事情可能会更复杂。你可以在你的soap客户端周围创建一个本地webserivce并使用tornado asyn http客户端向它发出异步请求???

这将创建一些复杂的回调逻辑,可以使用gen decorator

进行修复

答案 1 :(得分:0)

此问题自昨天起已修复。

拉请求: https://github.com/rancavil/tornado-webservices/pull/23

示例:这里是一个简单的Web服务,它不接受参数并返回版本。 请注意:

  • 方法声明:使用@gen.coroutine
  • 修饰方法
  • 返回结果:使用raise gen.Return(data)

代码:

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)

干杯!