Python 在TCP客户端是持久的情况下优化简单异步IO程序的方法

Python 在TCP客户端是持久的情况下优化简单异步IO程序的方法,python,python-3.x,optimization,python-asyncio,python-3.7,Python,Python 3.x,Optimization,Python Asyncio,Python 3.7,使用Python3.7.4和asyncio包,我试图编写一个应用程序,该应用程序将产生大约20000(20k或更多)个TCP客户端,然后连接到一台服务器。 然后,客户端等待来自服务器的命令(received_data=wait reader.read(4096))并继续执行它(wait loop.run_in_executor(…)),然后将响应发送回服务器(writer.write(resp)) 在这个循环完成后,我睡眠100ms(等待asyncio.sleep(100e-3)),以便允许其他

使用Python3.7.4和
asyncio
包,我试图编写一个应用程序,该应用程序将产生大约20000(20k或更多)个TCP客户端,然后连接到一台服务器。 然后,客户端等待来自服务器的命令(
received_data=wait reader.read(4096)
)并继续执行它(
wait loop.run_in_executor(…)
),然后将响应发送回服务器(
writer.write(resp)

在这个循环完成后,我睡眠100ms(
等待asyncio.sleep(100e-3)
),以便允许其他协同路由运行。 20k客户端永远不应该断开连接,应该无限期地处理来自服务器的命令

我对如何更改代码以优化它感兴趣(禁止使用
uvloop
或直接实现
协议
,因为我在uvloop的文档中看到了这可以提高性能),这超出了它现在的能力

假设我无法修改
处理请求

例如,
await asyncio.sleep(100e-3)
特别困扰我,但我不得不在那里添加它,否则给人的印象是,除了第一个,没有其他协同程序运行!为什么会这样? 假设我取消了睡眠(因为理论上其他
wait
s应该允许其他协同程序运行),我还能做什么

下面是我的应用程序的一个简单示例:

import asyncio
from collections import namedtuple
import logging
import os
import sys

logger = logging.getLogger(__name__)       
should_exit = asyncio.Event()


def exit(signame, loop):
    should_exit.set()
    logger.warning('Exiting soon...')


def handle_request(received_data, entity):
    logger.info('Backend logic here that consumes a bit of time depending on the entity and the received_data')


async def run_entity(entity, args):
    logger.info(f'Running entity {entity}')
    loop = asyncio.get_running_loop()

    try:        
        reader, writer = await asyncio.open_connection(args.addr[0], int(args.addr[1]))
        logger.debug(f'{entity} connected to {args.addr[0]}:{args.addr[1]}')
        try:
            while not should_exit.is_set():
                received_data = await reader.read(4096)

                if received_data:
                    logger.debug(f'{entity} received data {received_data}')

                    success, resp = await loop.run_in_executor(None, functools.partial(handle_request, received_data, entity))               

                    if success:
                        logger.debug(f'{entity} sending response {resp}')
                        writer.write(resp)
                        await writer.drain()

                await asyncio.sleep(100e-3)
        except ConnectionResetError:
            pass
    except ConnectionRefusedError:
        logger.warning(f'Connection refused by {args.addr[0]}:{args.addr[1]}.')
    except Exception:
        logger.exception('Details of unexpected error:')

    logger.info(f'Stopped entity {entity}')


async def main(entities, args):
    if os.name == 'posix':
        loop = asyncio.get_running_loop()
        loop.add_signal_handler(signal.SIGTERM, functools.partial(exit, signal.SIGTERM, loop))
        loop.add_signal_handler(signal.SIGINT, functools.partial(exit, signal.SIGINT, loop))

    tasks = (run_entity(entity, args) for entity in entities)
    await asyncio.gather(*tasks)


if __name__ == '__main__':
    ArgsReplacement = namedtuple('ArgsReplacement', ['addr'])
    asyncio.run(main(range(20000), ArgsReplacement(addr=['127.0.0.1', '4242'])))