python的高性能web应用的开发与测试实验

tornado“同步和异步”网络IO模型实验

引言

python语言一直以开发效率高著称,被广泛地应用于自动化领域:

  • 测试自动化
  • 运维自动化
  • 构建发布自动化

但是因为其也具有如下两个特征:

  1. 解释型语言
  2. GIL全局解释器锁

前者导致其性能天然就被编译型语言在性能上落后了许多。而后者则在多核并行计算时代,极大的限制了python的应用场景。

但是通过合理的web框架,则可以使用python扬长避短,仍然能够在多核并行时代须保持其高效开发的生产力同时,在性能上也有出色表现。例如,tornado框架。

tornado框架主要做了如下几件事:

  • 使用单线程的方式,避免线程切换的性能开销,同时避免在使用一些函数接口时出现线程不安全的情况
  • 支持异步非阻塞网络IO模型,避免主进程阻塞等待

前人实验

基于python语言的web框架众多,但是主流的有“Django”和“Tornado”基本上可以代表了它们的实现理念。

因为本文的重点是对 同步 和 异步 进行对比。所以关于不同web框架的性能对比实验,就引用一位网友的帖子的实验结果吧。

参考文章 [1]:轻量级web server Tornado代码分析

此文章有些部分写得比较简略,但是我们先大胆的做一下假设,作者是使用不同的python的web框架对最基本的 HelloWorld 代码进行了实现。

参考的Tornado实现如下:

import tornado.ioloop
import tornado.web class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world") application = tornado.web.Application([
(r"/", MainHandler),
]) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

最后使用 Apache Benchmark (ab),在另外一台机器上使用了如下指令进行负载测试:

ab -n 100000 -c 25 http://10.0.1.x/

在 AMD Opteron 2.4GHz 的四核机器上,结果如下图所示:

相较于第二快的服务器,Tornado在数据上的表现也是它的4倍之多。即使只用了一个CPU核的裸跑模式,Tornado也有33%的优势。

根据引文作者的观点:tornado是完虐其它的web框架的。

本文点评:此实验只是暂时让大伙建立一下宏观的对不同的web框架的性能的认识,至于可信度是存疑的,因为实验报告写得不太规范,细节省略太多。本文的观点是,如果都是采用同步的的写法,tornado和django的性能差异应该没有那么大的。当然这不太重要了,后面提到的 同步 和 异步 才是比较重要的。

下面则是本文的重点,同步和异步网络IO的性能测试和差异对比。

[1] 轻量级web server Tornado代码分析(http://blog.csdn.net/goldlevi/article/details/7047726)

测试环境

环境

  • CPU:core i3
  • 操作系统:Ubuntu 14.0
  • Python框架:py2.7
  • Web服务器:Tornado 4.2.0,服务器只启用一核心

内容

使用同步和异步的方式来写一段延时代码,然后再使用 apachebench进行压力测试:

  • 并发量 40
  • 总请求量 200

由于本文只是做性能对比,而不是性能的上限对比,所以都使用的是比较少的压力。

同步和异步代码

class SyncSleepHandler(RequestHandler):
"""
同步的方式,一个延时1s的接口
"""
def get(self):
time.sleep(1)
self.write("when i sleep 5s") class SleepHandler(RequestHandler):
"""
异步的延时1秒的接口
"""
@tornado.gen.coroutine
def get(self):
yield tornado.gen.Task(
tornado.ioloop.IOLoop.instance().add_timeout,
time.time() + 1
)
self.write("when i sleep 5s")

同步测试结果

➜  /  ab -n 200 -c 40 http://localhost:8009/demo/syncsleep-handler/
This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking localhost (be patient)
Completed 100 requests
Completed 200 requests
Finished 200 requests Server Software: TornadoServer/4.2.1
Server Hostname: localhost
Server Port: 8009 Document Path: /demo/syncsleep-handler/
Document Length: 15 bytes Concurrency Level: 40
Time taken for tests: 200.746 seconds
Complete requests: 200
Failed requests: 0
Total transferred: 42000 bytes
HTML transferred: 3000 bytes
Requests per second: 1.00 [#/sec] (mean)
Time per request: 40149.159 [ms] (mean)
Time per request: 1003.729 [ms] (mean, across all concurrent requests)
Transfer rate: 0.20 [Kbytes/sec] received Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.2 0 1
Processing: 1005 36235 18692.2 38133 200745
Waiting: 1005 36234 18692.2 38133 200745
Total: 1006 36235 18692.2 38133 200746 Percentage of the requests served within a certain time (ms)
50% 38133
66% 38137
75% 38142
80% 38161
90% 38171
95% 38176
98% 38179
99% 199742
100% 200746 (longest request)

异步测试结果

➜  /  ab -n 200 -c 40 http://localhost:8009/demo/sleep-handler/
This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking localhost (be patient)
Completed 100 requests
Completed 200 requests
Finished 200 requests Server Software: TornadoServer/4.2.1
Server Hostname: localhost
Server Port: 8009 Document Path: /demo/sleep-handler/
Document Length: 15 bytes Concurrency Level: 40
Time taken for tests: 5.083 seconds
Complete requests: 200
Failed requests: 0
Total transferred: 42000 bytes
HTML transferred: 3000 bytes
Requests per second: 39.35 [#/sec] (mean)
Time per request: 1016.611 [ms] (mean)
Time per request: 25.415 [ms] (mean, across all concurrent requests)
Transfer rate: 8.07 [Kbytes/sec] received Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.4 0 2
Processing: 1001 1010 12.0 1005 1053
Waiting: 1001 1010 12.0 1005 1053
Total: 1001 1010 12.3 1005 1055 Percentage of the requests served within a certain time (ms)
50% 1005
66% 1009
75% 1011
80% 1015
90% 1032
95% 1044
98% 1045
99% 1054
100% 1055 (longest request)

结果对比

在并发量为40,总请求量为200的简单的压力测试里面,两种网络IO模型的编程方式的性能对比如下:

同步和异步性能对比
性能指标 同步阻塞式 异步非阻塞式
每秒处理请求数(Requests per second) 1 39
请求平均等待时间-ms(Time per request,mean) 40149 1017
请求平均处理时间-ms(Time per request,across all ) 1003 25

测试的结果比较符合被测试程序的理论预期,因为被测试程序就功能就是:一个1s的延时等待。

显然:异步非阻塞式 和性能是远高于 同步阻塞式 的。

在上表中的 同步IO模型 数据里:只要是进入了单个请求的处理环节,进入到睡眠等待的 内核态 操作时,就会将整个进程给 阻塞,别的程序就只能进入 等待 状态了,这样本质上还是使用的 串行 的处理方式,所以 请求平均处理时间 大概是1000ms(1秒)左右,然后完成一个并发度为40的请求平均等待时间为40149ms。

关于上面参数的理解可以进行简单的类比解释。

以如下场景为例子:客户去银行处理业务的窗口办理业务。

  • 并行度:银行开设的服务窗口数和前台服务员

    对应CPU,窗口数对应着核心数,即真正的实现并行的能力,即不是在时间分片后交错进行的 “假象并行”

  • 并发度:大厅里面所有服务窗口等待服务的人数

    对应着单次的并发度,即本次作业需要处理的任务量

  • 总请求量:从银行大厅外面陆续过来加入到大厅队伍的客户的累计人数

  • 内核态操作:银行业务中必须只能由前台服务员处理的操作

  • 用户态操作:客户自己要处理的工作,比如:准备好自己的身份证,到外面复印证件,打电话和公司同事确认信息等等。

那么关于 同步 和 异步 的概念类比如下:

  • 同步阻塞系统:银行 没有 排队叫号系统 ,客户(Web服务器进程) 只能 在队伍人群里面傻等轮到自己,没有在排队时间干其它事的机会。随着外面的人不断地进入大厅,新请求的每个人都要等前面的队伍的全部处理完毕后( 40149ms)才能等到业务员(CPU)花1003ms 来处理自己的业务
  • 异步非阻塞系统:银行 有 排队叫号系统 ,客户有可以 不用 在拥挤的人群中傻等,旁边的休息区打开处理其它事情。客户直接领取叫号单据,花掉 5ms 递交准备材料(发起内核态操作请求) 要么收发邮件,要么看下小电影,然后等叫号系统叫自己后,立刻上去 20ms的时间解决掉问题。客户实际浪费在这上面的时间为 25ms ,当然银行业务员(CPU)还是要花 1000ms 去处理这个任务的

在这个假设的场景里面,不管是同步还是异步,业务员(CPU)都是 满负荷 的工作,但是却极大的节省了客户(web服务器进程) 的时间。这样客户自身可以把等待业务员响应的时间都利用起来做一些其它工作,这样就极大地提高了整体的工作效率。

众所周知,python有GIL,所以多线程其实是伪多线程。tornado于是就单进程只用单线程,不做线程切换,但是又要实现并行的方式,就全部使用异步了。只要是某个请求进入了内核态的耗时的IO操作,tornado的主进程在发起内核IO初始化之后就做不管它了,立刻回到web的监控中来去响应别的请求。等内核态的IO完成之后,再回调到用户态的主进程处理结果。如果是用同步模型,如果是使用单进程多线程,则会造成线程切换的开销,如果使用单进程单线程(像django一样),如果有一个请求比较耗时,第二个人的请求只会排队等候的,Web服务进程绝大多数情况都是被阻塞状态,性能就极大地降低了。

最后结合前面的延时1s的例子,再加一个即时响应的接口示例:

class JustNowHandler(tornado.web.RequestHandler):
def get(self):
self.write("i hope just now see you")

有兴趣的同学可以自己做实验。 事先约定:

  • 同步延时1s的接口为:A
  • 异步延时1s的接口为:B
  • 即时响应的接口为:C

使用单核模式运行web服务器。

然后在浏览器中以不同的顺序组合运行程序请求接口:

  • 先即时再延时
    • 先C再A:总共是1s后响应完毕C和A,C立刻响应
    • 先C再B:总共是1s后响应完毕C和B,C立刻响应
  • 先延时再即时
    • 先A再C:总共是1s后响应完毕C和A,C必须等A处理完毕后,才能在1s后响应
    • 先B再C:总共是1s后响应完毕C和B,C能立刻响应

同步模型中,一旦进程被阻塞掉,那么程序的效率就被等待的时间给严重降低了。

总结

有兴趣的同学,可以更深入的研究一下 《Unix网络编程-卷1,套接字联网API》(W.Richard Stevens) 的第6章第2节 I/O模型。

在python的web框架里面,tornado就是采用的最高效的异步非阻塞框架,可以在python语言下提供高性能的web应用服务。


作者: Harmo哈莫
作者介绍: https://zhengwh.github.io
技术博客: http://www.cnblogs.com/beer
Email: dreamzsm@gmail.com
QQ: 1295351490
时间: 2015-10
版权声明: 欢迎以学习交流为目的读者随意转载,但是请 【注明出处】
支持本文: 如果文章对您有启发,可以点击博客右下角的按钮进行 【推荐】

python的高性能web应用的开发与测试实验的更多相关文章

  1. 《Python Web 接口开发与测试》---即将出版

    为什么要出这样一本书? 首先,今年我有不少工作是跟接口自动化相关的,工作中的接口自动化颇有成效. 我一直是一个没有测试大格局的人,在各种移动测试技术爆发的这一年,我却默默耕耘着自己的一亩三分地儿(We ...

  2. web接口开发与测试

    最近一直在学习和整理web开发与接口测试的相关资料.接口测试本身毫无任何难度,甚至有很多工具和类库来帮助我们进行接口测试.大多测试人员很难深入了解web接口测试的原因是对web开发不太了解,当你越了解 ...

  3. 用Nginx+Lua(OpenResty)开发高性能Web应用

    在互联网公司,Nginx可以说是标配组件,但是主要场景还是负载均衡.反向代理.代理缓存.限流等场景:而把Nginx作为一个Web容器使用的还不是那么广泛.Nginx的高性能是大家公认的,而Nginx开 ...

  4. Nginx+Lua(OpenResty)开发高性能Web应用

    使用Nginx+Lua(OpenResty)开发高性能Web应用 博客分类: 跟我学Nginx+Lua开发 架构 ngx_luaopenresty 在互联网公司,Nginx可以说是标配组件,但是主要场 ...

  5. 高性能WEB开发(11) - flush让页面分块,逐步呈现

    高性能WEB开发(11) - flush让页面分块,逐步呈现 在处理比較耗时的请求的时候,我们总希望先让用户先看到部分内容,让用户知道系统正在进行处理,而不是无响应.一般大家在处理这样的情况,都使用a ...

  6. 使用Nginx+Lua(OpenResty)开发高性能Web应用

    摘自(http://jinnianshilongnian.iteye.com/blog/2280928) 在互联网公司,Nginx可以说是标配组件,但是主要场景还是负载均衡.反向代理.代理缓存.限流等 ...

  7. Python学习--20 Web开发

    HTTP格式 HTTP协议是基于TCP和IP协议的.HTTP协议是一种文本协议. 每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的. HTTP ...

  8. 【Python】【Web开发】

    # [[Web开发]] ''' 最早的软件都是运行在大型机上的,软件使用者通过“哑终端”登陆到大型机上去运行软件.后来随着PC机的兴起,软件开始主要运行在桌面上,而数据库这样的软件运行在服务器端,这种 ...

  9. pycharm+python+Django之web开发环境的搭建(windows)

    转载:https://blog.csdn.net/yjx2323999451/article/details/53200243/ pycharm+python+Django之web开发环境的搭建(wi ...

随机推荐

  1. UINavigationController push时,页面卡顿

    1.A push B A.view.backgroundColor 与 B.view.backgroundColor 不一致. 2. AssistiveTouch打开 关闭则不会出现卡顿情况 3.增加 ...

  2. 123. Best Time to Buy and Sell Stock (三) leetcode解题笔记

    123. Best Time to Buy and Sell Stock III Say you have an array for which the ith element is the pric ...

  3. MG--滚动的视觉差效果

    #几句代码完成tableView滚动的视觉差 - 效果图 (失帧严重)![](http://upload-images.jianshu.io/upload_images/1429890-f2c8577 ...

  4. win7环境下安装运行gotour【转载整理】

    转载请注明出处:http://www.cnblogs.com/Vulpers/p/5562586.html 最近尝试学习golang,在某个网站(真忘了)上发现gotour是一款灰常叼的教程& ...

  5. LoopBackJS 之 文件上传下载——使用loopback-component-storage

    参考链接: http://loopback.io/doc/en/lb2/Storage-component.html#creating-a-storage-component-data-source ...

  6. kali linux 、 windows、ubuntu三系统的引导问题

    '小飞机'是一个学生,所以接触的东西,虽广泛,但并不精通,在此利用随笔,记录自己的一些学习过程,以及自己的想法,既可以有时间自己复习,也可以顺便帮助别人. 近期由于同学的引诱以及男生天生对于破解的好奇 ...

  7. java连接Oracle数据库

    Oracle数据库先创建一个表和添加一些数据 1.先在Oracle数据库中创建一个student表: create table student ( id ) not null primary key, ...

  8. git 命令大全

    git init # 初始化本地git仓库(创建新仓库) git config --global user.name "xxx" # 配置用户名 git config --glob ...

  9. Win7 64位命令行编译cuda及设置Windows显卡响应时间

    在开始菜单中找到Visual Studio 2013 >> Visual Studio Tools 选择86或64版本的VC命令提示符环境,我用的 VS2013 x86 Native To ...

  10. Python :open文件操作,配合read()使用!

    python:open/文件操作 open/文件操作f=open('/tmp/hello','w') #open(路径+文件名,读写模式) 如何打开文件 handle=open(file_name,a ...