有可能用Python每秒处理120万个HTTP请求吗?是否有可能使用Python每秒处理数百万个HTTP请求?也许不是,但直到最近,情况一直如此。很多公司为了提高程序执行性能,降低服务器运营成本,正在放弃Python而选择其他编程语言。事实上,这并不是必须的,因为Python完全有能力完成这些任务。Python社区最近做了很多性能优化。CPython3.6重写新字典,全面提升解析器的执行性能。由于引入了更快的调用约定和字典查找缓存,CPython3.7甚至更快。我们可以使用PyPy的Just-in-Time来编译复杂的科学计算任务,NumPy的测试套件也优化了与C扩展的兼容性,PyPy也计划在今年晚些时候与Python3.5保持一致。这些激动人心的变化激发了我的创新精神,而Python擅长的领域有很多,我选择了其中之一:Web和微服务开发。了解Japronto!Japronto是一个全新的为微服务量身定制的微框架。实现它的主要目标包括足够快、可扩展和轻量级。的确,它快得吓人,甚至比NodeJS和Go还要快。感谢asyncio,我可以同时编写同步和异步代码。Python的微框架(蓝色)、NodeJS和Go(绿色)以及Japronto(紫色)的勘误表:用户@heppu提到如果您小心编写代码,Go的stdlibHTTP服务器的编写速度可以比上面的Go快12%。另外,fasthttp也是一个非常优秀的Go服务器,在同样的测试中其性能几乎只比Japronto低18%。太奇妙了!有关详细信息,请参阅https://github.com/squeaky-pl/japronto/pull/12和https://github.com/squeaky-pl/japronto/pull/14。我们可以看到Meinheld的WSGI服务器已经和NodeJS和Go的性能差不多了。虽然它使用了阻塞设计,但它仍然比前四个使用异步Python解决方案的速度快得多。因此,不要轻易相信别人声称异步系统总是比同步系统快的说法。虽然都是并发处理的问题,但事实远没有想象的那么简单。虽然我只是用“HelloWorld”完成了上面的微框架测试,但它清楚地展示了各种服务器框架的处理能力。这些测试是在具有8个VCPU的AmazonAWSEC2c4.2xlarge实例上完成的,数据中心选择在圣保罗地区,共享主机,HVM虚拟化,普通磁盘。操作系统是Ubuntu16.04.1LTS(XenialXerus),带有Linux4.4.0–53-genericx86_64内核。操作系统显示的CPU是Xeon?E5–2666v3@2.90GHz。我使用的Python版本是3.6,刚从源码编译而来。公平地说,包括Go在内的所有程序都只能在单个处理器内核上运行。测试工具是wrk,参数为1线程,100个连接,每个连接24个请求(共2400个并发请求)。HTTP流水线(图片来自维基百科)HTTP流水线在这里起着决定性的作用,因为Japronto使用它来优化并发请求的执行。大多数服务器对来自客户端的流水线和非流水线请求一视同仁,并以相同的方式处理它们,没有进行针对性的优化。(事实上??,Sanic和Meinheld也默默地把管道请求当成非管道,这违反了HTTP1.1协议。)简单来说,通过管道技术,客户端可以继续在同一个TCP连接上,而不需要等待服务器返回。发送后续请求。为了保证通信的完整性,服务器会按照请求的先后顺序,将结果一一返回给客户端。优化过程详解当一堆小的GET请求被客户端打包通过管道发送时,服务端可能只需要一次系统调用读取一个TCP数据包就可以得到所有的请求。系统调用以及在内核空间和用户空间之间移动数据比在进程内移动数据要昂贵得多。这就是为什么除非绝对必要,否则系统调用的次数应尽可能少。当Japronto接收到数据并成功解析出请求序列后,它会尝试尽可能快地执行请求,并以正确的顺序合并所有结果,然后只执行一个系统调用将数据发送给客户端。事实上,由于scatter/gatherIO等系统调用,合并工作不需要自己完成,只是Japronto还没有使用这些功能。然而,事情并不总是完美的,有时请求需要很长时间才能处理,等待完成会增加不必要的延迟。我们在做优化的时候,需要考虑系统调用的开销和请求的预期完成时间。经过优化后,Japronto得到了1,214,440RPS的分数。除了使用客户端管道请求和优化调用之外,还有一些其他可用的技术。Japronto几乎完全是用C写的,对象包括解析器、协议、链接管理、路由、请求、响应等都是用C扩展写的。Japronto试图实现Python的延迟加载。例如,协议头的字典只有在尝试请求时才会创建,而另一系列对象只有在第一次使用时才会创建。Japronto使用很棒的picohttpparserC库来解析状态、协议标头和碎片化的HTTP消息体。Picohttpparser直接调用现代CPU集成的SSE4.2扩展文本处理指令,快速匹配HTTP标签的边界(10年前的那些老x86_64CPU都有这个东东)。I/O使用了awesomeuvloop,它是libuv的一个包。在底层调用epoll提供异步读写通知。Picohttpparser依赖于SSE4.2和CMPESTRIx8664的特性进行解析_Python是一种具有垃圾回收功能的语言。为了避免不必要地增加垃圾收集器的压力,我们在设计高性能系统时必须多加注意。Japronto的内部设计尽量避免循环引用,尽可能少地分配和释放内存。它会预先申请一块区域来存放各种对象,同时在后续的请求中尽量复用那些没有继续引用的Python对象。而不是把那些东西扔掉。这些预分配内存的大小固定为4KB的倍数。内部结构会非常谨慎和频繁地使用这些连续的内存区域,以减少缓存失效的可能性。Japronto会尽量避免缓存之间不必要的复制,只在正确的地方执行操作。比如在处理路由时,先做URL解码,再进行路由匹配。开源贡献者,我需要你的帮助。三个多月以来,我一直在不断地开发Japronto,不仅在每个工作日,而且在周末。除了日常工作,我把所有的时间和精力都投入到这个项目中。我认为是时候与社区分享我的劳动成果了。Japronto可靠地实现了以下功能:实现HTTP1.x并支持分段上传完全支持HTTP流水线可配置是否保持链接Keep-alive支持同步和异步视图Master-multiworker多任务代码热加载易于使用路由规则下一次,我将深入研究Websockets和HTTP异步回复流量。写文档和做测试还有很多工作要做,如果你有兴趣加入我,请直接在推特上联系我。这是Japronto的GitHub项目存储库。同时,如果贵公司正在寻找熟悉性能优化的Python和DevOps工程师,我很乐意为您工作,在世界任何地方。结束语上面提到的所有技术不仅适用于Python,还可以适用于其他语言,例如Ruby、JavaScript,甚至PHP。我很想把这个付诸实践,但除非有人砸钱,否则我恐怕没有精力去做。
