使用Python的Treq on Twisted来进行HTTP压力测试


Posted in Python onApril 16, 2015

从事API相关的工作很有挑战性,在高峰期保持系统的稳定及健壮性就是其中之一,这也是我们在Mailgun做很多压力测试的原因。

这么久以来,我们已经尝试了很多种方法,从简单的ApacheBench到复杂些的自定义测试套。但是本贴讲述的,是一种使用python进行“快速粗糙”却非常灵活的压力测试的方法。
使用python写HTTP客户端的时候,我们都很喜欢用 Requests library。这也是我们向我们的API用户们推荐的。Requests 很强大,但有一个缺点,它是一个模块化的每线程一个调用的东西,很难或者说不可能用它来快速的产生成千上万级别的请求。
Treq on Twisted简介

为解决这个问题我们引入了Treq (Github库)。Treq是一个HTTP客户端库,受Requests影响,但是它运行在Twisted上,具有Twisted典型的强大能力:处理网络I/O时它是异步且高度并发的方式。

Treq并不仅仅限于压力测试:它是写高并发HTTP客户端的好工具,比如网页抓取。Treq很优雅、易于使用且强大。这是一个例子:

>>> from treq import get
  
 >>> def done(response):
 ...   print response.code
 ...   reactor.stop()
  
 >>> get("http://www.github.com").addCallback(done)
  
 >>> from twisted.internet import reactor
 200

简单的测试脚本
如下是一个使用Treq的简单脚本,用最大可能量的请求来对单一URL进行轰炸。

#!/usr/bin/env python
 from twisted.internet import epollreactor
 epollreactor.install()
  
 from twisted.internet import reactor, task
 from twisted.web.client import HTTPConnectionPool
 import treq
 import random
 from datetime import datetime
  
 req_generated = 0
 req_made = 0
 req_done = 0
  
 cooperator = task.Cooperator()
  
 pool = HTTPConnectionPool(reactor)
  
 def counter():
   '''This function gets called once a second and prints the progress at one
   second intervals.
   '''
   print("Requests: {} generated; {} made; {} done".format(
       req_generated, req_made, req_done))
   # reset the counters and reschedule ourselves
   req_generated = req_made = req_done = 0
   reactor.callLater(1, counter)
  
 def body_received(body):
   global req_done
   req_done += 1
  
 def request_done(response):
   global req_made
   deferred = treq.json_content(response)
   req_made += 1
   deferred.addCallback(body_received)
   deferred.addErrback(lambda x: None) # ignore errors
   return deferred
  
 def request():
   deferred = treq.post('http://api.host/v2/loadtest/messages',
              auth=('api', 'api-key'),
              data={'from': 'Loadtest <test@example.com>',
                 'to': 'to@example.org',
                'subject': "test"},
             pool=pool)
   deferred.addCallback(request_done)
   return deferred
  
 def requests_generator():
   global req_generated
   while True:
     deferred = request()
     req_generated += 1
     # do not yield deferred here so cooperator won't pause until
     # response is received
     yield None
  
 if __name__ == '__main__':
   # make cooperator work on spawning requests
   cooperator.cooperate(requests_generator())
  
   # run the counter that will be reporting sending speed once a second
   reactor.callLater(1, counter)
  
   # run the reactor
   reactor.run()

输出结果:

2013-04-25 09:30 Requests: 327 generated; 153 sent; 153 received
 2013-04-25 09:30 Requests: 306 generated; 156 sent; 156 received
 2013-04-25 09:30 Requests: 318 generated; 184 sent; 154 received

“Generated”类的数字代表被Twisted反应器准备好但是还没有发送的请求。这个脚本为了简洁性忽略了所有错误处理。为它添加超时状态的信息就留给读者作为一个练习。

这个脚本可以当做是一个起始点,你可以通过拓展改进它来自定义特定应用下的处理逻辑。建议你在改进的时候用collections.Counter 来替代丑陋的全局变量。这个脚本运行在单线程上,想通过一台机器压榨出最大量的请求的话,你可以用类似 mulitprocessing 的技术手段。

愿你乐在压力测试!

Python 相关文章推荐
Python实现的数据结构与算法之快速排序详解
Apr 22 Python
python使用Tkinter显示网络图片的方法
Apr 24 Python
在Python中使用PIL模块处理图像的教程
Apr 29 Python
详解Django缓存处理中Vary头部的使用
Jul 24 Python
Python实现调用另一个路径下py文件中的函数方法总结
Jun 07 Python
在python中利用GDAL对tif文件进行读写的方法
Nov 29 Python
强悍的Python读取大文件的解决方案
Feb 16 Python
浅谈Pytorch中的torch.gather函数的含义
Aug 18 Python
Django REST框架创建一个简单的Api实例讲解
Nov 05 Python
python文件编写好后如何实践
Jul 07 Python
Python ConfigParser模块的使用示例
Oct 12 Python
详解Scrapy Redis入门实战
Nov 18 Python
Python3中多线程编程的队列运作示例
Apr 16 #Python
使用Python脚本操作MongoDB的教程
Apr 16 #Python
使用Python中的greenlet包实现并发编程的入门教程
Apr 16 #Python
利用Python的Twisted框架实现webshell密码扫描器的教程
Apr 16 #Python
使用Python的Twisted框架实现一个简单的服务器
Apr 16 #Python
使用Python的Twisted框架编写简单的网络客户端
Apr 16 #Python
从Python的源码浅要剖析Python的内存管理
Apr 16 #Python
You might like
使用php批量删除数据库下所有前缀为prefix_的表
2014/06/09 PHP
php微信高级接口群发 多客服
2016/06/23 PHP
老生常谈ThinkPHP中的行为扩展和插件(推荐)
2017/05/05 PHP
Thinkphp5 微信公众号token验证不成功的原因及解决方法
2017/11/12 PHP
使用jQuery操作Cookies的实现代码
2011/10/09 Javascript
javascript操作Cookie(设置、读取、删除)方法详解
2015/03/18 Javascript
浅谈JavaScript中setInterval和setTimeout的使用问题
2015/08/01 Javascript
javascript同步服务器时间和同步倒计时小技巧
2015/09/24 Javascript
基于JavaScript实现定时跳转到指定页面
2016/01/01 Javascript
AngularJS入门心得之directive和controller通信过程
2016/01/25 Javascript
AngularJS基础 ng-selected 指令简单示例
2016/08/03 Javascript
Bootstrap基本组件学习笔记之进度条(15)
2016/12/08 Javascript
详解Angular.js数据绑定时自动转义html标签及内容
2017/03/30 Javascript
深入探究AngularJs之$scope对象(作用域)
2017/07/20 Javascript
angular2+node.js express打包部署的实战
2017/07/27 Javascript
JavaScript自执行函数和jQuery扩展方法详解
2017/10/27 jQuery
p5.js入门教程之键盘交互
2018/03/19 Javascript
Vue 与 Vuex 的第一次接触遇到的坑
2018/08/16 Javascript
详解Vue.js在页面加载时执行某个方法
2018/11/20 Javascript
在mpvue框架中使用Vant WeappUI组件库的注意事项【推进】
2019/06/09 Javascript
vue-property-decorator用法详解
2019/12/12 Javascript
在Vue中获取自定义属性方法:data-id的实例
2020/09/09 Javascript
python 统计代码行数简单实例
2017/05/04 Python
详解Python nose单元测试框架的安装与使用
2017/12/20 Python
Python并发编程协程(Coroutine)之Gevent详解
2017/12/27 Python
pygame游戏之旅 添加icon和bgm音效的方法
2018/11/21 Python
Django 反向生成url实例详解
2019/07/30 Python
pytorch构建多模型实例
2020/01/15 Python
django模型动态修改参数,增加 filter 字段的方式
2020/03/16 Python
django ObjectDoesNotExist 和 DoesNotExist的用法
2020/07/09 Python
中国排名第一的外贸销售网站:LightInTheBox.com(兰亭集势)
2016/10/28 全球购物
个人学习党的群众路线教育实践活动心得体会
2014/11/05 职场文书
公司2014年度工作总结
2014/12/10 职场文书
2015年度团总支工作总结
2015/04/23 职场文书
2015年新教师个人工作总结
2015/10/14 职场文书
Android中View.post和Handler.post的关系
2022/06/05 Java/Android