Python问题:无法在独立线程或事件循环中运行connexion aiohttp服务器
问题复现与解决方案
我一直无法将使用aiohttp的connexion服务器放到后台运行,导致启动web服务器时主线程会被阻塞。我知道可以通过asyncio为其余逻辑创建任务实现需求,但我正在开发供其他个人和企业复用的库,无法单方面强制所有使用者统一采用这种设计模式。
我已经实现了原生aiohttp服务的后台运行,代码如下:
import asyncio import threading from aiohttp import web import time def aiohttp_server(): def say_hello(request): return web.Response(text='Hello, world') app = web.Application(debug=True) app.add_routes([web.get('/', say_hello)]) handler = app.make_handler() return handler def run_server(handler): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) server = loop.create_server(handler, host='127.0.0.1', port=8089) loop.run_until_complete(server) loop.run_forever() t = threading.Thread(target=run_server, args=(aiohttp_server(),)) t.start() for i in range(50000): print('Hi from Main Thread') time.sleep(5)
上述代码可以正常启动服务,同时主线程可以继续执行其他逻辑。但我尝试将该方案适配到connexion服务器时遇到了问题,运行后主线程不会被阻塞,但web服务器完全不响应任何请求,我的实现代码如下:
def initializeServerTest(): app = connexion.AioHttpApp(__name__, port=8080, specification_dir='openAPISpecs/', only_one_api=True ) app.add_api('openAPISpec.json', arguments={'title': 'Instrument and HUB API'}, pythonic_params=True, validate_responses=True ) # 配置CORS cors = aiohttp_cors.setup(app.app, defaults={ "*": aiohttp_cors.ResourceOptions(allow_credentials=True, expose_headers="*", allow_headers="*", allow_methods=["GET", "POST", "PUT"] ) }) for route in list(app.app.router.routes()): cors.add(route) runner = web.AppRunner(app.create_app()) return runner def run_server(runner): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) loop.run_until_complete(runner.setup()) site = web.TCPSite(runner, 'localhost', 8080) loop.run_until_complete(site.start()) loop.run_forever() def StartServerInBackground(): t = threading.Thread(target=run_server, args=(initializeServerTest(),)) t.start()
问题原因
- 创建AppRunner时错误调用了
app.create_app(),该方法会生成全新的空白aiohttp应用,之前添加的API路由、CORS配置全部没有被应用到最终运行的服务中,导致没有任何请求可以被匹配处理。 - 所有依赖asyncio的对象(包括AioHttpApp实例、AppRunner等)都应该在后台线程对应的专属事件循环中创建,原代码在主线程初始化connexion应用,和后台线程的事件循环不属于同一个上下文,会导致异步对象调用异常。
修复后的实现代码
import asyncio import threading import time import connexion import aiohttp_cors from aiohttp import web def run_server(): # 所有异步相关初始化都放在后台线程的事件循环上下文内执行 loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) # 初始化connexion应用 app = connexion.AioHttpApp(__name__, port=8080, specification_dir='openAPISpecs/', only_one_api=True ) app.add_api('openAPISpec.json', arguments={'title': 'Instrument and HUB API'}, pythonic_params=True, validate_responses=True ) # 配置CORS规则 cors = aiohttp_cors.setup(app.app, defaults={ "*": aiohttp_cors.ResourceOptions(allow_credentials=True, expose_headers="*", allow_headers="*", allow_methods=["GET", "POST", "PUT"] ) }) for route in list(app.app.router.routes()): cors.add(route) # 直接使用已经配置完成的app.app创建Runner,不要调用create_app() runner = web.AppRunner(app.app) loop.run_until_complete(runner.setup()) site = web.TCPSite(runner, 'localhost', 8080) loop.run_until_complete(site.start()) loop.run_forever() def StartServerInBackground(): # 新增daemon=True,避免主进程退出时后台线程挂死 t = threading.Thread(target=run_server, daemon=True) t.start() # 测试逻辑 if __name__ == "__main__": StartServerInBackground() for i in range(50000): print('Hi from Main Thread') time.sleep(5)
内容的提问来源于stack exchange,提问作者Brian
相关产品推荐
相关产品推荐

