4

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

  1. (通过 SOAPUI)对操作 A 的外部请求
  2. 操作 B 中的内部请求(通过请求模块)
  3. 操作 B 的内部响应
  4. 操作 A 的外部响应

因为它都在一个服务中运行,所以它在某个地方被阻止了。我不熟悉龙卷风的异步功能。

只有一种请求处理方法(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 和 get_temp)。因此 SOAPUI 命中 get_temp,它向 get_currency 发出 SOAP 请求(通过 make_curr_request 和 requests 模块)。然后结果应该只是链回并被发送回 SOAPUI。

服务的实际操作没有任何意义(当要求温度时返回货币)但我只是想让功能正常工作,这些是我拥有的操作。

4

2 回答 2

7

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

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

您可以使用tornados AsynHttpClient来切换它。这几乎可以用作请求的精确替代品。从文档示例:

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

他们的方法用 async 装饰,并且他们正在发出异步 http 请求。这是流程变得有点奇怪的地方。当您使用 AsyncHttpClient 时,它不会锁定事件循环(请让我这周刚开始使用龙卷风,如果我的所有术语都不正确,请放轻松)。这允许服务器自由处理传入的请求。当您的 asynchttp 请求完成时,回调方法将被执行,在这种情况下on_response

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

这将创建一些可以使用gen装饰器修复的复杂回调逻辑

于 2013-02-01T17:31:43.180 回答
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)

干杯!

于 2014-12-29T21:56:28.043 回答