【问题标题】:Python3 asyncio - running two servers in own threadPython3 asyncio - 在自己的线程中运行两台服务器
【发布时间】:2019-04-07 21:49:52
【问题描述】:

我的服务器类继承自 BaseServer:

class BaseServer(object):

    def __init__(self, host, port):
        self.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(self.loop)

        self.instance = asyncio.start_server(self.handle_connection, host = host, port = port)

    async def handle_connection(self, reader: StreamReader, writer: StreamWriter):
        pass

    def start(self):
        # wrapping coroutine into ensure_future to allow it to call from call_soon
        # wrapping into lambda to make it callable
        callback = asyncio.ensure_future(self.instance)
        self.loop.call_soon(lambda: callback)
        self.loop.run_forever()
        self.loop.close()

    def stop(self):
        self.loop.call_soon_threadsafe(self.loop.stop)

    @staticmethod
    def get_instance():
        return BaseServer(None, None)

我需要两个在自己的线程中运行的服务器来并行处理请求。 但是当我尝试根据需要运行它们时,只有第一台服务器正在运行。下面是我如何运行它们:

if __name__ == '__main__':
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)

    async def run():
        pool = ThreadPoolExecutor(max_workers=cpu_count())

        await loop.run_in_executor(pool, Server1.get_instance().start)
        await loop.run_in_executor(pool, Server2.get_instance().start)

    loop.run_until_complete(run())
  1. 我做错了什么?如何在自己的线程中运行每个服务器?
  2. asyncio.set_event_loopdef __init__ 调用时,出现下一个错误:

RuntimeError: 线程 'Thread-1' 中没有当前事件循环。

但是如果我从def __init__ 中删除asyncio.set_event_loop 并将其移动到def start,错误就会消失。为什么会这样?

【问题讨论】:

  • 在不同的线程中运行服务器并没有真正得到任何东西。在同一个循环中运行多个服务器不会阻止请求同时处理。
  • @Vincent 但是通过 run_until_complete 在同一个循环块中运行两台服务器,以便其中一台服务器处理对另一台服务器的请求,不是吗?如何正确运行两台服务器?

标签: python python-3.5 python-multithreading python-asyncio


【解决方案1】:

跟进 OP 的评论:

但是通过 run_until_complete 在同一个循环中运行两台服务器会阻止其中一台服务器循环处理对另一台服务器的请求,不是吗?如何正确运行两台服务器?

这里是 python 3.5 的 asyncio 文档中 TCP server example 的修改版本:

# Start server 1
coro1 = asyncio.start_server(handle_echo, '127.0.0.1', 8888, loop=loop)
server1 = loop.run_until_complete(coro1)
print('Serving 1 on {}'.format(server1.sockets[0].getsockname()))

# Start server 2
coro2 = asyncio.start_server(handle_echo, '127.0.0.1', 8889, loop=loop)
server2 = loop.run_until_complete(coro2)
print('Serving 2 on {}'.format(server2.sockets[0].getsockname()))

# Serve requests until Ctrl+C is pressed
try:
    loop.run_forever()
except KeyboardInterrupt:
    pass

# Close the servers
server1.close()
loop.run_until_complete(server1.wait_closed())
server2.close()
loop.run_until_complete(server2.wait_closed())

# Close the loop
loop.close()

请注意,在 asyncio 中添加了 python 3.7 后,它看起来像 much nicer

async def main():
    server1 = await asyncio.start_server(
        handle_echo, '127.0.0.1', 8888)

    addr1 = server1.sockets[0].getsockname()
    print(f'Serving 1 on {addr1}')

    server2 = await asyncio.start_server(
        handle_echo, '127.0.0.1', 8889)

    addr2 = server2.sockets[0].getsockname()
    print(f'Serving 2 on {addr2}')

    async with server1, server2:
        await asyncio.gather(
            server1.serve_forever(), server2.serve_forever())

asyncio.run(main())

【讨论】:

  • 知道了!非常感谢
  • 很好地升级到 py3.7 成语。您可能希望最后一行类似于 await asyncio.gather(server1.serve_forever(), server2.serve_forever()) 以获得最大的正确性。
  • @user4815162342 我其实很想知道,为什么你认为gather 的方法更正确?
  • gather 捕捉到等待两台服务器停止服务的意图,而await server1.serve_forever() 让我们看起来像是在等待server1 并且不在乎关于server2。另一种选择是将asyncio.waitFIRST_COMPLETED 一起使用并取消另一个,但输入太多了。在实践中,它们之间几乎没有什么区别,因为预计这两个服务器都不会孤立地停止服务。
  • 网络应用也可以做到这一点吗? (aiohttp.web) 而不是一个简单的 tcp 服务器?我无法弄清楚片段,所以它适用于收集。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-23
  • 2019-01-20
  • 1970-01-01
  • 1970-01-01
  • 2018-12-23
相关资源
最近更新 更多