异步操作是计算机软硬件系统中的一个普遍概念,根源在于参与协作的实体在处理速度上存在明显差异。软件开发中遇到的情况大多是CPU和IO的速度不匹配,所以异步IO存在于各种编程框架中,比如客户端比如浏览器,服务端比如node.js。本文主要分析Python异步IO。Python3.4标准库新增了一个模块asyncio,用于支持异步IO,但目前的API状态是临时的,这意味着不能保证向后兼容,甚至可能会从标准库中移除(可能性很小)。如果你关注PEP和Python-Dev,你会发现这个模块已经酝酿了很久,后续API和实现上可能会有调整,但毫无疑问asyncio非常实用和强大,而且值得学习和研究。asyncio示例主要处理TCP/UDPsocket通信,从容管理大量连接,无需创建大量线程,提高系统运行效率。这里对官方文档中的一个例子进行简单修改,实现一个HTTP长连接基准测试工具,用于诊断WEB服务器的长连接处理能力。功能概述:每10毫秒创建10个连接,直到达到目标连接数(比如10k),每个连接会定时向服务器发送HEAD请求,维护HTTPkeepavlie。代码如下:importargparseimportasyncioimportfunctoolsimportloggingimportrandomimporturllib.parseloop=asyncio.get_event_loop()@asyncio.coroutinedefprint_http_headers(no,url,keepalive):url=urllib.parse.urlsplit(url)wait_for=functools.partial(asyncio.wait_for=loop,timeout=3loop)query=('HEAD{url.path}HTTP/1.1\r\n''Host:{url.hostname}\r\n''\r\n').format(url=url).encode('utf-8')rd,wr=yieldfromwait_for(asyncio.open_connection(url.hostname,80))whileTrue:wr.write(query)whileTrue:line=yieldfromwait_for(rd.readline())ifnotline:#endofconnectionwr.close()returnnoline=line.decode('utf-8').rstrip()ifnotline:#endofheaderbreaklogging.debug('(%d)HTTPheader>%s'%(no,line))yieldfromasyncio.sleep(random.randint(1,keepalive//2))@asyncio.coroutinedefdo_requests(args):conn_pool=set()waiter=asyncio.Future()def_on_complete(fut):conn_pool.remove(fut)exc,res=fut.exception(),fut。结果()ifexcisnotNone:logging.info('conn#{}exception'.format(exc))else:logging.info('conn#{}result'.format(res))ifnotconn_pool:waiter.set_result('eventloopisdone')foriinrange(args.connections):fut=asyncio.async(print_http_headers(i,args.url,args.keepalive))fut.add_done_callback(_on_complete)conn_pool。添加(fut)ifi%10==0:yieldfromasyncio.sleep(0.01)logging.info((yieldfromwaiter))defmain():parser=argparse.ArgumentParser(description='asyncli')parser.add_argument('url',帮助='pageaddress')parser.add_argument('-c','--connections',type=int,default=1,help='numberofconnectionssimultaneously')parser.add_argument('-k','--keepalive',类型=int,default=60,help='HTTPkeepalivetimeout')args=parser.parse_args()logging.basicConfig(level=logging.INFO,format='%(asctime)s%(message)s')loop.run_until_complete(do_requests(args))loop.close()if__name__=='__main__':main()测试分析硬件:CPU2.3GHz/2核,RAM2GB软件:CentOS6.5(kernel2.6.32),Python3.3(pipinstallasyncio),nginx1.4.7参数设置:ulimit-n10240;nginxworker连接数改为10240启动WEB服务器,只需要一个worker进程:#../sbin/nginx#psax|grepnginx2007?ss0:00nginx:masterprocess../sbin/nginx2008?S0:00nginx:workerprocess启动benchmark工具,发起10k连接,目标URL为nginx默认测试页面:$pythonasyncli.pyhttp://10.211.55.8/-c10000nginx日志统计平均每秒请求数:#tail-1000000access.log|awk'{print$4}'|sort|uniq-c|awk'{cnt+=1;sum+=$1}END{printf"avg=%d\n",sum/cnt}'avg=548toppartoftheoutput:VIRTRESSHRS%CPU%MEMTIME+COMMAND657m115m3860R60.26.24:30.02python5420810m848R7.00.60:30.79nginx总结:1.Python实现简洁明了不到80几行代码,只用了标准库,逻辑很直观。想象一下,C/C++标准库实现了这些功能,突然觉得“人生苦短,我用Python”。2、Python运行效率不理想。当连接建立后,客户端和服务端的数据发送和接收逻辑是类似的。看上面的top输出,Python的CPU和RAM使用率基本是nginx的10倍,也就是说效率相差100倍(CPUxRAM)。侧面展示了Python与C的效率差距。虽然这个比较有点极端,但毕竟nginx不仅使用了C还深度优化了CPU/RAM使用率,但类似任务的效率相差两个数量级,除非是BUG,说明架构设计的出发点不一样,Python的可读性和易用性。第二是性能。Nginx是一个高度优化的网络服务器。开发一个模块很麻烦。重用它的异步框架就更难了。开发效率和运营效率之间总会有一个权衡。3.单线程异步IOvs.多线程同步IO。上面的例子是一个单线程的异步IO。其实不写demo就知道多线程同步IO效率低很多。每个线程有一个连接?10k个线程,光是线程栈就占用600+MB(64KB*10000)内存,再加上线程上下文切换和GIL,基本就是噩梦。asyncio的核心概念下面是学习asyncio需要了解的四个核心概念。详情请见<参考文献>1。事件循环。单线程异步的关键就在于这个高层事件循环,它是同步执行的。2.未来。异步IO由许多异步任务组成,每个异步任务由一个future控制。3.协程。每个异步任务的具体执行逻辑都由协程来体现。4.发电机(产量和产量)。在asyncio中大量使用,是一个不容忽视的语法细节。参考资料1.asyncio–异步I/O、事件循环、协程和任务,https://docs.python.org/3/library/asyncio.html2.PEP3156,重新启动异步IO支持:“asyncio”模块,http://legacy.python.org/dev/peps/pep-3156/3。PEP380,委派给子生成器的语法,http://legacy.python.org/dev/peps/pep-0380/4。PEP342,通过增强型生成器实现协程,http://legacy.python.org/dev/peps/pep-0342/5。PEP255,简单生成器,http://legacy.python.org/dev/peps/pep-0255/6。异步源代码,http://hg.python.org/cpython/file/3.4/Lib/asyncio/
