首页 > 解决方案 > Python - 无法通过 aiohttp 使用多个事件循环

问题描述

我正在尝试制作与此类似的代码。


class FooFoo:
    def __init__(self):
        loop = asyncio.get_event_loop()
        self.value = loop.run_until_complete(self.async_work())

    async def async_work(self):
        return 10


def build_server():
    app = web.Application()
    app.router.add_route('GET', '/', hello)
    web.run_app(app, 'localhost', '12000')

async def hello(request):
    foo = await FooFoo()
    return web.json_response{'ip': request.remote, 'value': foo.value}


执行 a 会curl http://127.0.0.1:/产生此错误:

Error handling request
Traceback (most recent call last):
  File "/usr/local/lib64/python3.6/site-packages/aiohttp/web_protocol.py", line 418, in start
    resp = await task
  File "/usr/local/lib64/python3.6/site-packages/aiohttp/web_app.py", line 458, in _handle
    resp = await handler(request)
  File "/usr/local/lib64/python3.6/site-packages/aiohttp/web_urldispatcher.py", line 155, in handler_wrapper
    result = old_handler(request)
  File "test.py", line 36, in hello
    foo = asyncio.get_event_loop().run_until_complete(FooFoo())
  File "test.py", line 24, in __init__
    self.value = loop.run_until_complete(self.async_work())
  File "/usr/lib64/python3.6/asyncio/base_events.py", line 471, in run_until_complete
    self.run_forever()
  File "/usr/lib64/python3.6/asyncio/base_events.py", line 425, in run_forever
    raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running

发生这种情况是因为服务器正在事件循环上运行并且FooFoo想要重新运行循环。

我尝试过的解决方案:

RuntimeError: Cannot run the event loop while another loop is running

我需要能够运行多个循环,或者如果您有更好的解决方案,我会接受它。

如果有帮助,我正在使用 python 3.6.8

标签: pythonpython-3.xaiohttpaiopython-asyncio

解决方案


如果您将逻辑稍微更改为如下所示:

import asyncio

from aiohttp import web


class FooFoo(object):

    async def async_work(self):
        await asyncio.sleep(1)
        self.value = 10


async def hello(request):
    foo = FooFoo()
    await foo.async_work()

    return web.json_response({'ip': request.remote, 'value': foo.value})


def build_server():
    app = web.Application()
    app.router.add_route('GET', '/', hello)
    web.run_app(app, host='localhost', port=8080)

你会得到它的工作。

更新鉴于OP评论的详细信息:

Foo Foo 类是通过对服务器的异步请求生成令牌的客户端。用户不应该实例化 FooFoo,然后再生成一个令牌。

我建议使用构建器模式来简化复杂对象的创建。我认为这种模式很适合这种情况。为了应用该模式,我们添加了一个新FooFooBuilder类:

async def request_token():
    await asyncio.sleep(1)

    return 42


class FooFooBuilder(object):
    @staticmethod
    async def build():
        token = await request_token()

        # Do token validation and error handling. 

        return FooFoo(token)

该类FooFootoken在实例化期间将其作为参数:

class FooFoo(object):
    def __init__(self, token):
        self.value = token

这样hello请求处理程序将更改为:

async def hello(request):
    foo = await FooFooBuilder.build()

    return web.json_response({'ip': request.remote, 'value': foo.value})

使用这种方法,无需使用多个事件循环来完成任务。


推荐阅读