51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 3915|回复: 0
打印 上一主题 下一主题

[转贴] python的高性能web应用的开发与测试实验

[复制链接]
  • TA的每日心情
    无聊
    昨天 09:34
  • 签到天数: 1052 天

    连续签到: 2 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2021-1-25 09:40:07 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    引言
      python语言一直以开发效率高著称,被广泛地应用于自动化领域:
      测试自动化
      ·维自动化
      ·构建发布自动化
      但是因为其也具有如下两个特征:
      1. 解释型语言
      2. GIL全局解释器锁
      前者导致其性能天然就被编译型语言在性能上落后了许多。而后者则在多核并行计算时代,极大的限制了python的应用场景。
      但是通过合理的web框架,则可以使用python扬长避短,仍然能够在多核并行时代须保持其高效开发的生产力同时,在性能上也有出色表现。例如,tornado框架。
      tornado框架主要做了如下几件事:
      ·使用单线程的方式,避免线程切换的性能开销,同时避免在使用一些函数接口时出现线程不安全的情况
      ·支持异步非阻塞网络IO模型,避免主进程阻塞等待
      前人实验
      基于python语言的web框架众多,但是主流的有“Django”和“Tornado”基本上可以代表了它们的实现理念。
      参考的Tornado实现如下:
    1. import tornado.ioloop
    2.   import tornado.web
    3.   
    4.   class MainHandler(tornado.web.RequestHandler):
    5.       def get(self):
    6.           self.write("Hello, world")
    7.   
    8.   application = tornado.web.Application([
    9.       (r"/", MainHandler),
    10.   ])
    11.   
    12.   if __name__ == "__main__":
    13.       application.listen(8888)
    14.       tornado.ioloop.IOLoop.instance().start()
    复制代码
    最后使用 Apache Benchmark (ab),在另外一台机器上使用了如下指令进行负载测试:

    1. ab -n 100000 -c 25 http://10.0.1.x/
    复制代码
    在 AMD Opteron 2.4GHz 的四核机器上,结果如下图所示:


      相较于第二快的服务器,Tornado在数据上的表现也是它的4倍之多。即使只用了一个CPU核的裸跑模式,Tornado也有33%的优势。
      根据引文作者的观点:tornado是完虐其它的web框架的。
      本文点评:此实验只是暂时让大伙建立一下宏观的对不同的web框架的性能的认识,至于可信度是存疑的,因为实验报告写得不太规范,细节省略太多。本文的观点是,如果都是采用同步的的写法,tornado和django的性能差异应该没有那么大的。当然这不太重要了,后面提到的 同步 和 异步 才是比较重要的。
      测试环境
      环境
      ·CPU:core i3
      ·操作系统:Ubuntu 14.0
      ·Python框架:py2.7
      ·Web服务器:Tornado 4.2.0,服务器只启用一核心
      内容
      使用同步和异步的方式来写一段延时代码,然后再使用 apachebench进行[url=]压力测试[/url]:
      ·并发量 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")

    同步测试结果

    1.    /  ab -n 200 -c 40 http://localhost:8009/demo/syncsleep-handler/
    2.   This is ApacheBench, Version 2.3 <$Revision: 1528965 [        DISCUZ_CODE_40        ]gt;
    3.   Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
    4.   Licensed to The Apache Software Foundation, http://www.apache.org/
    5.   
    6.   Benchmarking localhost (be patient)
    7.   Completed 100 requests
    8.   Completed 200 requests
    9.   Finished 200 requests
    10.   
    11.   
    12.   Server Software:        TornadoServer/4.2.1
    13.   Server Hostname:        localhost
    14.   Server Port:            8009
    15.   
    16.   Document Path:          /demo/syncsleep-handler/
    17.   Document Length:        15 bytes
    18.   
    19.   Concurrency Level:      40
    20.   Time taken for tests:   200.746 seconds
    21.   Complete requests:      200
    22.   Failed requests:        0
    23.   Total transferred:      42000 bytes
    24.   HTML transferred:       3000 bytes
    25.   Requests per second:    1.00 [#/sec] (mean)
    26.   Time per request:       40149.159 [ms] (mean)
    27.   Time per request:       1003.729 [ms] (mean, across all concurrent requests)
    28.   Transfer rate:          0.20 [Kbytes/sec] received
    29.   
    30.   Connection Times (ms)
    31.               min  mean[+/-sd] median   max
    32.   Connect:        0    0   0.2      0       1
    33.   Processing:  1005 36235 18692.2  38133  200745
    34.   Waiting:     1005 36234 18692.2  38133  200745
    35.   Total:       1006 36235 18692.2  38133  200746
    36.   
    37.   Percentage of the requests served within a certain time (ms)
    38.   50%  38133
    39.   66%  38137
    40.   75%  38142
    41.   80%  38161
    42.   90%  38171
    43.   95%  38176
    44.   98%  38179
    45.   99%  199742
    46.   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模型的编程方式的性能对比如下:



      测试的结果比较符合被测试程序的理论预期,因为被测试程序就功能就是:一个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的例子,再加一个即时响应的接口示例:
    1. class JustNowHandler(tornado.web.RequestHandler):
    2.       def get(self):
    3.           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应用服务。





    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-28 02:01 , Processed in 0.067436 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表