使用tornado为swagger生成的服务器提供异步端点

使用tornado为swagger生成的服务器提供异步端点,swagger,python-asyncio,tornado,Swagger,Python Asyncio,Tornado,我已经使用swagger编辑器生成了一个服务器。然后,我将使用Tornado作为http服务器,如: def main(): app = App(system_manager=system_manager, import_name=__name__, specification_dir='./swagger/', server='tornado') app.app.json_encoder = encoder.JSONEncoder app.a

我已经使用swagger编辑器生成了一个服务器。然后,我将使用Tornado作为http服务器,如:

def main():
    app = App(system_manager=system_manager, import_name=__name__,
              specification_dir='./swagger/', server='tornado')
    app.app.json_encoder = encoder.JSONEncoder
    app.add_api('swagger.yaml', arguments={
                'title': 'API'}, pythonic_params=True)
    app.run(port=8085)
应用程序所在位置:

class App(connexion.App):
def __init__(self, system_manager, import_name, server='tornado', **kwargs):
    super(App, self).__init__(import_name, server=server, **kwargs)
    if not issubclass(type(system_manager), SystemManager):
        raise ValueError(
            "App.init: 'system_manager' is not a subclass of 'SystemManager'")
    self.__system_manager = system_manager

def run(self, port=None, server=None, debug=None, host=None, **options):
    server_type = server or self.server
    if server_type != 'tornado':
        super(App, self).run(port=port, server=server,
                             debug=debug, host=host, **options)
        return None
    if port is not None:
        self.port = port
    elif self.port is None:
        self.port = 5000

    self.host = host or self.host or '0.0.0.0'

    if server is not None:
        self.server = server

    if debug is not None:
        self.debug = debug

    wsgi_container = tornado.wsgi.WSGIContainer(self.app)
    http_server = tornado.web.Application([
        (r'/websocket/.*', WebSocket, dict(system_manager=self.__system_manager)),
        (r'^/v1/wifi(/all)*$', AsyncFallbackHandler,
         dict(fallback=wsgi_container)),
        (r'.*', tornado.web.FallbackHandler, dict(fallback=wsgi_container))
    ], websocket_ping_interval=5)
    http_server.listen(self.port, address=self.host)
    tornado.ioloop.IOLoop.instance().start()
由于某些原因,我有一些端点需要30秒才能响应,而且因为我使用的是
WSGIContainer
,所以所有请求都是同步的。这意味着在这些请求之后提交的每个请求都将被处理,直到完成为止。引用文件:

WSGI是一个同步接口,而Tornado的并发模型是 基于单线程异步执行。这意味着 使用Tornado的WSGIContainer运行WSGI应用程序的可伸缩性不如 在多线程WSGI服务器(如gunicorn或 uwsgi

我曾尝试:

  • 继续使用
    WSGIContainer
    ,但使用的处理程序将使调用异步。没有成功。我得到:
    RuntimeError:线程“ThreadPoolExecutor-0\u 0”中没有当前事件循环。
  • 创建另一个不使用WSGIContainer的RequestHandler。但在这里,当请求为404时,它无法对json编码
    ConnexionResponse
    。我也不能将
    ConnexionResponse
    写入管道,因为它必须是string/bytes/dict

  • 请帮助我找到一种方法,使我的一些端点异步

    我通过解包ConnexionResponse并设置状态代码,使我的第二个解决方案能够工作。以下是修复后的情况:

    async def run_in_executor(self, method, *args):
        loop = tornado.ioloop.IOLoop.instance().asyncio_loop
        done, pending = await asyncio.wait(
            fs=[loop.run_in_executor(None, method, args)],
            return_when=asyncio.ALL_COMPLETED
        )
        result = done.pop().result()
        if type(result) is ConnexionResponse:
            self.set_status(result.status_code)
            return result.body
        enc = JSONEncoder()
        return enc.encode(result)
    
    class WifiRequestHandler(tornado.web.RequestHandler):
        async def get(self, *args, **kwargs):
            # await tornado.gen.sleep(20)
            ids = self.get_arguments('ids')
            method = get_wifi
            if self.request.path.startswith('/v1/wifi/all'):
                method = get_wifi_all
    
            self.write(await self.run_in_executor(method, ids))
            self.set_header('Content-Type', 'application/json')
    
        async def post(self, *args, **kwargs):
            logger.info(kwargs)
            body = tornado.escape.json_decode(self.request.body)
            self.write(await self.run_in_executor(update_wifi, body))
            self.set_header('Content-Type', 'application/json')
    
        async def run_in_executor(self, method, *args):
            loop = tornado.ioloop.IOLoop.instance().asyncio_loop
            done, pending = await asyncio.wait(
                fs=[loop.run_in_executor(None, method, args)],
                return_when=asyncio.ALL_COMPLETED
            )
            result = done.pop().result()
            if type(result) is ConnexionResponse:
                return result
            enc = JSONEncoder()
            return enc.encode(result)
    
    async def run_in_executor(self, method, *args):
        loop = tornado.ioloop.IOLoop.instance().asyncio_loop
        done, pending = await asyncio.wait(
            fs=[loop.run_in_executor(None, method, args)],
            return_when=asyncio.ALL_COMPLETED
        )
        result = done.pop().result()
        if type(result) is ConnexionResponse:
            self.set_status(result.status_code)
            return result.body
        enc = JSONEncoder()
        return enc.encode(result)