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. OpenMP并行构造的schedule子句详解 (转载)

    原文:http://blog.csdn.net/gengshenghong/article/details/7000979 schedule的语法为: schedule(kind, [chunk_si ...

  2. html之间的传值

    传值:window.location.href=“eidit.html?activityId=“+acytivityIDd: 将id放进地址栏传到另一html页面 接受 再用var  str=wind ...

  3. linux下svn常用命令

    (如果是第一次提交文件,很可能会出现“svn:'.'不是工作副本”,即当前目录不是工作副本,这个时候需要用到import: eg:svn import . url) 1.将文件checkout到本地目 ...

  4. 没有QQ的日子

    说来,也怪电脑不好,一开QQ就卡,年级也不小了,QQ上真的没啥话好说的,所以就想着关闭QQ. 其实做软件的知道,很多事情不是订下规则就可以做的到的,不过我还是给自己定个规则: 过完农历年后就不用QQ了 ...

  5. sicily 1007. To and Fro 2016 11 02

    // Problem#: 1007// Submission#: 4893204// The source code is licensed under Creative Commons Attrib ...

  6. Asp.net使用代码修改配置文件的节点值

    使用代码修改配置文件的方法: 1.打开配置文件写入的权限 2.先按节点名称长到要修改的节点,然后删除,紧接着将有新值的节点添加回去 3.关闭配置文件写入的权限 修改Appsetting节点的值,修改其 ...

  7. Python爬虫入门案例:获取百词斩已学单词列表

    百词斩是一款很不错的单词记忆APP,在学习过程中,它会记录你所学的每个单词及你答错的次数,通过此列表可以很方便地找到自己在记忆哪些单词时总是反复出错记不住.我们来用Python来爬取这些信息,同时学习 ...

  8. 基于 Winform + DotNetBar 写的股市行情助手

    StockViewer 股市行情助手 简介 观看股市行情,窗口太显眼,是否担心被身后的老板发现? 窗口来回切换,工作时每隔几分钟就要看一眼股市.难道只能同时做一件事情吗? 现在,一款完全免费.开源的小 ...

  9. C#自旋的艺术

    CODE1: while (status == ServerStatus.Started) { if (*TaskLocker > 0) { for (int i = 0; i != *Leve ...

  10. uva 11401 Triangle Counting

    // uva 11401 Triangle Counting // // 题目大意: // // 求n范围内,任意选三个不同的数,能组成三角形的个数 // // 解题方法: // // 我们设三角巷的 ...