Python WSGI Server 性能分析
上一篇 中,介绍了主要的Python WSGI web服务器,在本篇,我们将尝试对主要的几款产品级WSGI server进行benchmark测试。这篇仍然是翻译文章,原文地址: https://blog.appdynamics.com/engineering/a-performance-analysis-of-python-wsgi-servers-part-2/ 。
CGI和mod_python怎么样?
上篇文章中我们曾提到,WSGI出现之前,主要的两个python web App部署方案是CGI和mod_python。CGI通过为每一个web请求开启新进场的方式运行,这种运行方式效率较差,相比之下,mod_python运行效率明显好过CGI,但是mod_python只支持Apache,而且现在mod_python项目的开发进度也不活跃。所以,WSGI是目前Python web App部署最流行也是最好的可选方案。
参与测试的WSGI Server
由于时间限制,仅仅测试以下普遍认为最为流行的6个WSGI server。所有的测试代码在 GitHub 上。
- Bjoern 号称“令人惊叹的超快Python WSGI server”,并且认为它是“最快、最小、最轻量的WSGI Server”。我们用最常用的库开发了这个小型 Web App 用于测试。
- CherryPy 是一个非常流行和稳定的WSGI框架,以及Web框架(实际上Web框架是它的主打,WSGI server是附带产品)。用于测试的 Web App 代码 。
- Gunicorn <http://gunicorn.org/> 名称来源于Ruby的Unicorn项目。它的自我定位是“实现简单,轻量级,可观的运行效率”。与Bjoern和CherryPy不同,Gunicorn是一个标准的Web server。我们使用以下 命令 运行Gunicorn的测试。其中,参考官方文档的描述,“WORKER_COUNT”设置成 CPU数*2 + 1。
- Meinheld 是一个轻量、快速的WSGI server。参考其官网的示例,我们构建了`测试代码 <https://gist.github.com/omedhabib/d638e213af0f843580e5ca7724005ac6>`_ 。
- mod_wsgi 的作者同事也是 mod_python 的作者。和mod_python一样,它仅仅支持Apache。但它还包括一个 mod_wsgi-express的小工具,可以运行一个最小化的Apache实例,而mod_wsgi则可以运行在这个Apache实例上。我们采用mod_wsgi-express,使用以下 配置命令 构建测试。与Gunicorn一样,我们设置了 CPU数*2 + 1 的worker进程数量。
- uWSGI 是一个功能全面的 Web App 服务器,常见的部署方案中,uWSGI前端配置一个Nginx的反向代理。参考mod_wsgi和Gunicorn,uWSGI也配置了 CPU数*2 + 1 的worker进程数量,参考代码 。
准备和测试细节
为了让测试尽可能不受系统因素影响,我们创建了一个 Docker容器 来启动测试,以此确保所有的测试都是运行在一个干净的系统中。
服务器配置
- 标准化的 Docker 容器
- 2核心CPU
- 512M内存
测试过程
- 使用 wrk(HTTP benchmark工具) 执行测试
- 按照随机顺序发起对各个WSGI服务器的测试,连接数逐次增加,范围在100到10000之间。
- wrk 运行在2核CPU的docker上
- 每次测试持续30秒,重复4次
测试输出
- 稳定请求数、错误量、延迟,三个指标的平均值均来自于 wrk 输出的数据
- Docker’s stat tool provided the high CPU and memory watermarks.
- Docker的统计来自于自带的CPU和内存分析工具
- 抛弃最高和最低的数值,用剩余数据求均值
- 如果你想了解更多详细内容,我们把完整的数据和代码放在 GitHub
测试结果
所有的原始输出都在测试项目的 代码仓库 中,同时对测试结果整理生成的 CSV 也在代码仓库中。如果你希望看到 CSV 生成的图表结果,可以在这个 Google文档 查看。
处理请求数
下图是处理请求数的平均值,数字越大越好。纵轴 - 每秒处理请求数,横轴 - 并发连接数。
- Bjoern:明显胜出
- CherryPy:尽管是纯Python开发,但表现非常出色
- Meinheld:表现相当可观,但docker容器的资源使用相当低
- mod_wsgi: 并不是最快的,但运行稳定
- Gunicorn:很好的运行效率且资源好用也很好,但并发数支持还有待提高
- uWSGI:测试结果相当失望
胜出者:Bjoren
Bjoern
从处理的稳定请求数来看,Bjoern显然是测试中最优秀的胜出者。但是,从数字上看,Bjoern居然比其它WSGI Server超过了非常多,这让我们有一点怀疑测试的正确性。一开始,我们按照参与测试的WSGI Server的首字母顺序进行测试,因此我们考虑是不是因为Bjoern最先开始测试而获得了我们没有发现的资源上的优势,但是即使我们再多次随机运行所有测试,结果仍然没有明显的变化,那么这个测试结果应该是真实反映了实际情况。
uWSGI
本来我们以为uWSGI会在测试结果中位于较好的一组,但最终却对uWSGI的表现非常失望,它几乎是最慢的WSGI Server。我们注意到uWSGI在测试中一直打印日志信息到屏幕上,所以我们考虑他是不是因为打印日志而影响了执行效率,但在通过 - -disable-loging 选项禁用日志打印重新测试后,uWSGI表现依然没有什么变化。
uWSGI文档中提到,它通常需要使用Nginx设置一个反向代理来作为前端服务器,但我们不确定设置这个反向代理后会对它的运行效率有大幅提升(译者补充:通常uWSGI和Nginx配合使用时,是通过一个uWSGI定义的TCP协议通信,而原文测试是用uWSGI的HTTP模式,不确定这个是否会有影响)。
延迟
延迟是测试一个请求和响应之间的耗时,数字越小越好。纵轴 - 毫秒,横轴 - 并发连接数。
- CherryPy:运行的非常好,耗时基本稳定在3毫秒每个请求
- Bjoern:基本上延迟很低,但是并发数较低时表现的更好
- Gunicorn:延迟数字很稳定,表现不错
- mod_wsgi:所有WSGI server中表现中档,但即便在并发很高时延迟数字也很稳定
- Meinheld:总的来说,表现尚可,但并发数提升时表现开始下滑
- uWSGI:又是表现最差的
胜出者:CherryPy
内存使用
比较各框架的内存使用情况,内存数字越小越好。纵轴 - 兆(M),横轴 - 并发数。
- Bjoern:非常轻量级,仅仅需要9MB内存处理10000个并发请求
- Meinheld:跟Bjoern的表现很接近
- Gunicorn:内存使用数量非常稳定,并发数增加时内存变化很小,而内存总耗用量相对也比较少
- CherryPy:并发小时内存比较少,但是会随着并发数增加而递增
- mod_wsgi:内存耗用相对其它框架较多,但还算稳定
- uWSGI:很明显内存耗用有些问题
胜出者:Bjoern和Meinheld
错误
对一个web server来说,通常会发生的错误是:服务器丢失连接,中止连接,超时。错误数量越少越好。纵轴 - 错误发生次数,横轴 - 并发数。
所有的错误数字是通过比例计算得到(因响应总数各个WSGI server可能不一致)
- CherryPy:几乎保持为0,即使并发数非常高的时候
- Bjoern:会发生错误,同时会随着响应请求数发生变化
- mod_wsgi:表现不错,在并发最高点大约6%的错误率
- Gunicorn:在并发数较高时会发生9%的错误率,略显不足
- uWSGI:在并发数较高时发生约34%的错误率
- Meinheld:并发高时表现很差,从图上开已经超过10000的错误数(几近于完全不可用)
胜出者:CherryPy
CPU使用
高CPU使用率并不能说明其不好或者好,也不能说明WSGI server运行的良莠。但是,CPU使用率也能体现WSGI server内部运行的一些情况。由于测试的docker容器分配了两个CPU,所以CPU使用率的最大数字是200%。纵轴 - CPU使用率,横轴 - 并发数。
- Bjoern:单线程运行,通过其最大CPU使用率是100%可以证明
- CherryPy:多线程运行但是最高直到150%,可能受Python GIL 的影响
- Gunicorn:支持多进程运行,可以看到在低并发时就开始有200%的CPU使用率
- Meinheld:单线程运行,跟Bjoern相似
- mod_wsgi:多线程运行,在低并发时就开始使用200%的CPU
- uWSGI:CPU使用一直较低,更多的证明uWSGI配置可能不正确
胜出者:无,主要是通过CPU使用率来观察WSGI server的一些运行情况
总结
通过测试,得到了好几个令人意外的结果。首先,Bjoern的表现非常亮眼,但我们也怀疑它太超过其它WSGI server的表现是否合理。第二,uWSGI表现太让人失望。也许我们对uWSGI的配置不优化,或者我们安装的uWSGI版本不正确。
总结所有WSGI server的测试结果:
- Bjoern:显然做到了它宣称的“最快、最小、最轻量的WSGI Server”
- CherryPy:运行快,轻量,错误率低,作为Python原生开发的WSGI server,表现不错
- Gunicorn:稳定、较快的运行效率,资源耗用在所有WSGI server中排中段
- Meinheld:运行很好,资源耗用低,但是并发高时表现很不好
- mod_wsgi:和Apache集成,表现很好
参考资源:
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.