多个aiohttp Application()在同一个进程中运行?

Fra*_*k T 13 python python-asyncio aiohttp

两个aiohttp.web.Application()对象可以在同一个进程中运行,例如在不同的端口上运行吗?

我看到一堆aiohttp代码示例:

from aiohttp import web
app = web.Application()
app.router.add_get('/foo', foo_view, name='foo')
web.run_app(app, host='0.0.0.0', port=10000)
Run Code Online (Sandbox Code Playgroud)

我想知道是否有一些等价物web.Applications()可以配置多个同时运行.就像是:

from aiohttp import web
app1 = web.Application()
app1.router.add_get('/foo', foo_view, name='foo')
app2 = web.Application()
app2.router.add_get('/bar', bar_view, name='bar')
# This is the wishful thinking code:
web.configure_app(app1, host='0.0.0.0', port=10000)
web.configure_app(app2, host='0.0.0.0', port=10001)
web.run_apps()
Run Code Online (Sandbox Code Playgroud)

我的用例是我有一个现有的python web框架来做这种事情,我正在构建一个类似于python 3.6和aiohttp的原型.

我知道多个python服务器可以运行在例如nginx之后(另请参阅http://aiohttp.readthedocs.io/en/stable/deployment.html); 那不是我追求的.我想探索两个具有相同asyncio事件循环的aiohttp Web服务器的可能性,在同一个python进程中运行,在两个不同的端口上提供服务.

kwa*_*nek 11

是的,你可以 - 只需编写一些重新实现的包装器run_app.

这是一个简单的例子.所有特定于应用程序的部分run_app都将移至专用类AppWrapper.该MultiApp只负责初始化所有配置的应用程序,继续运行循环和清理.

import asyncio
from aiohttp import web


class AppWrapper:

    def __init__(self, aioapp, port, loop):
        self.port = port
        self.aioapp = aioapp
        self.loop = loop
        self.uris = []
        self.servers = []

    def initialize(self):
        self.loop.run_until_complete(self.aioapp.startup())
        handler = self.aioapp.make_handler(loop=self.loop)

        server_creations, self.uris = web._make_server_creators(
            handler, loop=self.loop, ssl_context=None,
            host=None, port=self.port, path=None, sock=None,
            backlog=128)

        self.servers = self.loop.run_until_complete(
            asyncio.gather(*server_creations, loop=self.loop)
        )

    def shutdown(self):
        server_closures = []
        for srv in self.servers:
            srv.close()
            server_closures.append(srv.wait_closed())
        self.loop.run_until_complete(
            asyncio.gather(*server_closures, loop=self.loop))

        self.loop.run_until_complete(self.aioapp.shutdown())

    def cleanup(self):
         self.loop.run_until_complete(self.aioapp.cleanup())

    def show_info(self):
        print("======== Running on {} ========\n".format(', '.join(self.uris)))


class MultiApp:    

    def __init__(self, loop=None):
        self._apps = []
        self.user_supplied_loop = loop is not None
        if loop is None:
            self.loop = asyncio.get_event_loop()
        else:
            self.loop = loop

    def configure_app(self, app, port):
        app._set_loop(self.loop)
        self._apps.append(
            AppWrapper(app, port, self.loop)
        )

    def run_all(self):
        try:
            for app in self._apps:
                app.initialize()
            try:
                for app in self._apps:
                    app.show_info()
                print("(Press CTRL+C to quit)")
                self.loop.run_forever()
            except KeyboardInterrupt:  # pragma: no cover
                pass
            finally:
                for app in self._apps:
                    app.shutdown()
        finally:
            for app in self._apps:
                app.cleanup()

        if not self.user_supplied_loop:
            self.loop.close()
Run Code Online (Sandbox Code Playgroud)

注意:请注意使用内部aiohttp方法,可能会有所变化.

现在让我们使用它:

from aiohttp import web

async def handle1(request):
    return web.Response(text='SERVER 1')


async def handle2(request):
    return web.Response(text='SERVER 2')

app1 = web.Application()
app1.router.add_get('/', handle1)

app2 = web.Application()
app2.router.add_get('/', handle2)

ma = MultiApp()
ma.configure_app(app1, port=8081)
ma.configure_app(app2, port=8071)
ma.run_all()
Run Code Online (Sandbox Code Playgroud)

作为旁注,请再想一想为什么需要这个.在几乎所有情况下,解耦是更好的选择.在同一进程中设置多个端点使它们相互依赖.有一种情况出现在我的脑海中并具有"好"的推理,内部统计/调试端点.


Bry*_*att 8

看起来 3.0 版增加了一种以前没有的更好的方法来做到这一点:https : //aiohttp.readthedocs.io/en/stable/web_advanced.html#aiohttp-web-app-runners

编辑

文档(正如所指出的)有点不清楚(我自己也不得不与之抗争)。要在多个端口上运行多个服务器,只需为每个站点重复文档中的代码。简而言之,您需要为要在单独的地址/端口上运行的每个单独的应用程序/服务器创建一个应用程序、AppRunner(并称之为setup())和一个 TCPSite(并称之为start())。

最简单的方法是为重复的站点设置创建一个异步函数,然后可以将您的应用程序实例和端口传递给它。我还包括提到的在循环退出时对跑步者的清理。

希望这可以帮助。

import asyncio
from aiohttp import web

runners = []

async def start_site(app, address='localhost', port=8080):
    runner = web.AppRunner(app)
    runners.append(runner)
    await runner.setup()
    site = web.TCPSite(runner, address, port)
    await site.start()

loop = asyncio.get_event_loop()

loop.create_task(start_site(web.Application()))
loop.create_task(start_site(web.Application(), port=8081))
loop.create_task(start_site(web.Application(), port=8082))

try:
    loop.run_forever()
except:
    pass
finally:
    for runner in runners:
        loop.run_until_complete(runner.cleanup())
Run Code Online (Sandbox Code Playgroud)