使用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 中文字符串的处理实现代码
Oct 25 Python
Python通过RabbitMQ服务器实现交换机功能的实例教程
Jun 29 Python
详解Python中的静态方法与类成员方法
Feb 28 Python
TF-IDF与余弦相似性的应用(二) 找出相似文章
Dec 21 Python
Python 使用PIL numpy 实现拼接图片的示例
May 08 Python
Python生成器generator用法示例
Aug 10 Python
Python数据可视化教程之Matplotlib实现各种图表实例
Jan 13 Python
python实现的分层随机抽样案例
Feb 25 Python
Python使用Socket实现简单聊天程序
Feb 28 Python
使用keras实现孪生网络中的权值共享教程
Jun 11 Python
Python selenium键盘鼠标事件实现过程详解
Jul 28 Python
Python实现简单的俄罗斯方块游戏
Sep 25 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 操作符与控制结构
2012/03/07 PHP
详谈PHP编码转换问题
2015/07/28 PHP
CodeIgniter常用知识点小结
2016/05/26 PHP
php验证身份证号码正确性的函数
2016/07/20 PHP
老生常谈ThinkPHP中的行为扩展和插件(推荐)
2017/05/05 PHP
JavaScript实现快速排序(自已编写)
2012/12/19 Javascript
js 火狐下取本地路径实现思路
2013/04/02 Javascript
JS对象转换为Jquery对象示例
2014/01/26 Javascript
javascript:window.open弹出窗口的位置问题
2014/03/18 Javascript
javascript正则匹配汉字、数字、字母、下划线
2014/04/10 Javascript
jquery UI Datepicker时间控件的使用方法(基础版)
2015/11/07 Javascript
js 去掉字符串前后空格实现代码集合
2017/03/25 Javascript
Angular.js实现动态加载组件详解
2017/05/28 Javascript
jquery引入外部CDN 加载失败则引入本地jq库
2018/05/23 jQuery
vue watch关于对象内的属性监听
2019/04/22 Javascript
js遍历详解(forEach, map, for, for...in, for...of)
2019/08/28 Javascript
JS基础之逻辑结构与循环操作示例
2020/01/19 Javascript
python使用urllib2模块获取gravatar头像实例
2013/12/18 Python
python基于Tkinter库实现简单文本编辑器实例
2015/05/05 Python
利用numpy+matplotlib绘图的基本操作教程
2017/05/03 Python
Python实现读取Properties配置文件的方法
2018/03/29 Python
使用Django启动命令行及执行脚本的方法
2018/05/29 Python
python求绝对值的三种方法小结
2019/12/04 Python
利用PyCharm操作Github(仓库新建、更新,代码回滚)
2019/12/18 Python
python matplotlib库的基本使用
2020/09/23 Python
Python实现给PDF添加水印的方法
2021/01/25 Python
css3实现可滑动跳转的分页插件示例
2014/05/08 HTML / CSS
英国在线照明超市:Castlegate Lights
2019/10/30 全球购物
俄罗斯最大的灯具网站:Fandeco
2020/03/14 全球购物
同学会邀请书大全
2014/01/12 职场文书
篝火晚会策划方案
2014/05/16 职场文书
暖通工程师岗位职责
2014/06/12 职场文书
纪律教育学习心得体会
2014/09/02 职场文书
2014年电教工作总结
2014/12/19 职场文书
用JS实现飞机大战小游戏
2021/06/09 Javascript
Python基础之条件语句详解
2021/06/16 Python