魔术堆栈
uvloop:快速的Python网络连接
作者Yury Selivanov @ 1st1
2016年5月3日
TL; DR
异步和uvloop
PEP 3156引入的asyncio模块是网络传输,协议和流抽象的集合,带有可插入事件循环。事件循环是异步的核心。它提供以下API:
- 安排电话
- 通过网络传输数据,
- 执行DNS查询,
- 处理操作系统信号,
- 方便的抽象来创建服务器和连接,
- 异步处理子流程。
uvloop是内置asyncio事件循环的替代产品。您可以使用pip安装uvloop:
$ pip安装uvloop
在异步代码中使用uvloop很简单:
导入异步 导入 uvloop asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
上面的代码片段使任何asyncio.get_event_loop()
调用都返回uvloop的实例。
建筑
uvloop用Cython编写,并建立在libuv之上。
libuv是供nodejs使用的高性能,多平台异步I / O库。由于nodejs的普及和流行,libuv既快速又稳定。
uvloop实现所有异步事件循环API。高级Python对象包装了低级libuv结构和函数。继承用于保持代码DRY并确保任何手动内存管理与libuv原语的寿命保持同步。
基准测试
为了检查uvloop的性能如何与其他实现相提并论,我们创建了一个工具台来对TCP和UNIX套接字I / O以及HTTP协议的性能进行基准测试。
标有基准的服务器在带有外部负载生成工具(HTTP基准的wrk)的Docker容器中运行,该工具可以测量请求的吞吐量和延迟。
本博客文章中的所有基准测试均在3.70GHz的Intel Xeon CPU E5-1620 v2和Ubuntu Linux上运行。我们使用Python 3.5,所有服务器均为单线程。另外,我们使用GOMAXPROCS=1
Go代码,nodejs不使用集群,并且所有Python服务器都是单进程的。每个基准设置TCP_NODELAY
标志。
Mac OS X上的基准测试结果非常相似。
TCP协议
此基准测试了具有不同消息大小的简单回显服务器的性能。我们使用1、10和100 KiB软件包。并发级别为10。每个基准测试运行30秒。
关于每个职位的一些评论:
- ASYNCIO流。asyncio及其内置的纯Python事件循环。在此基准测试中,我们测试了高级流抽象的性能。我们
asyncio.create_server()
用来创建将一对传递(reader, writer)
给客户端协程的服务器。 - 龙卷风。该服务器实现了一个非常简单的Tornado协议,该协议会立即将收到的所有数据发送回去。
- 古玩流。Curio是Python aio lib块上的新手。与asyncio-streams相似,在此基准测试中,我们测试curio流,
curio.make_streams()
用于创建一对(reader, writer)
提供高级API(例如)的流readline()
。 - 扭曲。与龙卷风类似,这里我们测试最小回波协议。
- 古玩。此基准测试了古玩插槽的性能:
sock.recv()
和sock.sendall()
协程的紧密循环。 - uvloop流。与#2一样,这里我们测试asyncio高级流的性能,但是这次是在uvloop之上。
- gevent。我们使用
gevent.StreamServer
gevent套接字在紧密的循环中发送/接收数据。 - ASYNCIO。看来,香草异步反应相当快!与#2和#4类似,这里我们测试在纯Python异步中实现的最小回显协议的性能。
- nodejs。我们使用
net.createServer
API在nodejs v4.2.6中测试流的性能。 - uvloop。此基准测试了在uvloop上以异步方式实现的最小回显协议(如#2,#4,#8)。有了1 KiB消息,uvloop是最快的实现,每秒高达105,000个请求!对于100 KiB消息,uvloop设法以大约2.3 GiB / s的速度运行。
- 围棋。
net.Conn.Read/Write
通话紧密。Golang的性能与uvloop非常相似,对于10和100 KiB消息,性能稍好一些。
HTTP
最初,我们想针对nodejs和Go在asyncio和uvloop上测试aiohttp。aiohttp是使用asyncio编写异步HTTP服务器和客户端的最受欢迎的框架。
出乎意料的是,借助高性能HTTP解析器,纯Python异步比使用相同HTTP解析器的nodejs更快!
Go对于1 KiB响应更快,但是uvloop + asyncio对于10/100 KiB响应明显更好。服务质量对于使用httptools的asyncio和uvloop以及Go来说都是极好的。
诚然,与其他实现不同,基于httptools的服务器非常小,并且不包含任何路由逻辑。但是,该基准测试表明了使用有效实施的协议可以实现多快的uvloop。
结论
可以肯定地得出结论,使用uvloop,可以编写Python网络代码,每个CPU内核每秒可以推送成千上万的请求。在多核系统上,可以使用进程池进一步扩展性能。
uvloop和asyncio,再加上Python 3.5 中async / await的强大功能,使使用Python编写高性能网络代码比以往任何时候都更加容易。
请尝试uvloop(github)并与我们分享您的结果!