使用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 httplib模块使用实例
Apr 11 Python
深入解析Python设计模式编程中建造者模式的使用
Mar 02 Python
Python基础教程之正则表达式基本语法以及re模块
Mar 25 Python
python3实现zabbix告警推送钉钉的示例
Feb 20 Python
Python日期时间Time模块实例详解
Apr 15 Python
python子线程退出及线程退出控制的代码
Oct 16 Python
python绘制BA无标度网络示例代码
Nov 21 Python
Python2与Python3的区别点整理
Dec 12 Python
django ObjectDoesNotExist 和 DoesNotExist的用法
Jul 09 Python
如何使用python socket模块实现简单的文件下载
Sep 04 Python
python爬虫中PhantomJS加载页面的实例方法
Nov 12 Python
Python数据可视化之基于pyecharts实现的地理图表的绘制
Jun 10 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
自己动手,丰衣足食 - 短波框形天线制作
2021/03/01 无线电
解析php扩展php_curl.dll不加载的解决方法
2013/06/26 PHP
php中用memcached实现页面防刷新功能
2014/08/19 PHP
php中fgetcsv()函数用法实例
2014/11/28 PHP
php的闭包(Closure)匿名函数详解
2015/02/22 PHP
PHP 搜索查询功能实现
2016/11/29 PHP
php解析非标准json、非规范json的方式实例
2020/12/10 PHP
JavaScript 常用函数库详解
2009/10/21 Javascript
js中判断文本框是否为空的两种方法
2011/07/31 Javascript
Ajax异步提交表单数据的说明及方法实例
2013/06/22 Javascript
JS中使用apply、bind实现为函数或者类传入动态个数的参数
2016/04/26 Javascript
Javascript DOM事件操作小结(监听鼠标点击、释放,悬停、离开等)
2017/01/20 Javascript
axios取消请求的实践记录分享
2018/09/26 Javascript
react-native滑动吸顶效果的实现过程
2019/06/03 Javascript
Vue解析剪切板图片并实现发送功能
2020/02/04 Javascript
Element Badge标记的使用方法
2020/07/27 Javascript
[51:53]DOTA2-DPC中国联赛 正赛 RNG vs Dragon BO3 第二场 1月24日
2021/03/11 DOTA
Python实现PS滤镜功能之波浪特效示例
2018/01/26 Python
Python格式化输出字符串方法小结【%与format】
2018/10/29 Python
django mysql数据库及图片上传接口详解
2019/07/18 Python
Python操作MySQL数据库实例详解【安装、连接、增删改查等】
2020/01/17 Python
python爬取王者荣耀全皮肤的简单实现代码
2020/01/31 Python
python3 中时间戳、时间、日期的转换和加减操作
2020/07/14 Python
CSS3中31种选择器使用方法教程
2013/12/05 HTML / CSS
幼儿园元旦亲子活动方案
2014/02/17 职场文书
中式结婚主持词
2014/03/14 职场文书
质量标语大全
2014/06/12 职场文书
老人节标语大全
2014/10/08 职场文书
2014年法务工作总结
2014/12/11 职场文书
车队安全员岗位职责
2015/02/15 职场文书
2015年小学英语教师工作总结
2015/05/12 职场文书
2015年房产销售工作总结范文
2015/05/22 职场文书
埃及王子观后感
2015/06/16 职场文书
初中政治教学工作总结
2015/08/13 职场文书
用golang如何替换某个文件中的字符串
2021/04/25 Golang
浅谈MySql整型索引和字符串索引失效或隐式转换问题
2021/11/20 MySQL