Python3 asyncio - запуск двух серверов в собственном потоке - PullRequest
0 голосов
/ 04 ноября 2018

Мои классы серверов унаследованы от 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_loop звонит с def __init__ Я получил следующую ошибку:

RuntimeError: В потоке 'Thread-1' нет текущего цикла событий.

Но если я уберу asyncio.set_event_loop из def __init__ и переместу его в def start, ошибка исчезнет. Почему это случилось?

1 Ответ

0 голосов
/ 04 ноября 2018

После комментария ОП:

Но запуск двух серверов с помощью run_until_complete в одном и том же цикле циклов блокирует один из серверов для обработки запроса к другому, не так ли? Как правильно запустить два сервера?

Вот модифицированная версия примера TCP-сервера из документации asyncio для python 3.5:

# 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()

Обратите внимание, что при добавлении Python 3.7 в Asyncio, это будет выглядеть намного лучше :

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())
...