用Python做大批量请求发送
原创. 禁转.
大批量请求发送需要考虑的几个因素:
1. 服务器承载能力(网络带宽/硬件配置);
2. 客户端IO情况, 客户端带宽, 硬件配置;
方案:
1. 方案都是相对的;
2. 因为这里我的情况是客户机只有一台,所以不能考虑使用分布式了, 服务器承载能力也非常有限(经过不断调试得知);
3. 这里没有使用Jmeter, 虽然jmeter也是可以做到的.
注: 如无特殊说明以下代码基于windows7 64位/Centos 6.5 64位, Python3.6+
Python里面支持发送大批量的方案有很多, 这里只介绍我所用过的几种:
1. 使用grequests:
grequests可以一次性发送超大批量的请求, 但是底层听说修改了socket通信, 可能不稳定或者不安全? 而且如果你需要校验对比每个请求的发送信息与返回信息, 比较不方便, 因为它是批量发送,然后批量收回, 示例代码 :
import grequests
import time
from collections import OrderedDict
import hashlib
import os
import xlrd
import json
import datetime class voiceSearchInterface:
@classmethod
def formatUrlAndHeader(self, des, singer, songName):
#生成url和header的逻辑
return url, h @classmethod
def exeRequests(self):
errorLog = open("errorLog.txt","w+")
startTime = datetime.datetime.now()
rightCount = 0
errCount = 0
descr = ["播放", "搜索", "搜", "听", "我要听", "我想听", "来一首", "来一个", "来一段", "来一曲", "来首", "来个", "来段", "来曲"]
orgPath = os.path.join(os.path.dirname(os.getcwd()), "test","SongsAndSingers","singersAndSongs3.txt")
f = open(orgPath,"rb")
i = 0
urlsAndHs = []
for line in f.readlines():
temp = line.decode().split("\t")
orgSinger = temp[0]
orgSong = temp[1].replace("\n","")
for k in descr:
urlAndH = self.formatUrlAndHeader(k, orgSinger,orgSong)
urlsAndHs.append(urlAndH)
f.close()
rs = (grequests.get(u[0], headers = u[1], stream = False) for u in urlsAndHs)
rsText = grequests.imap(rs, size=20)
for r in rsText:
executingLog = open("Log.txt","w+")
i+=1
try:
searchResult = json.loads(r.text)
searchItem = searchResult["data"]["searchitem"]
tt = searchItem.split("Split")
searchSinger = tt[1]
searchSong = tt[-1]
resultSinger = searchResult["data"]["sounds"][0]["singer"]
resultSong = searchResult["data"]["sounds"][0]["title"]
if(searchSinger==resultSinger and searchSong==resultSong): rightCount += 1
else: errCount += 1
print(searchSinger, "\t",resultSinger, "\t",searchSong,"\t", resultSong)
except Exception:
errCount += 1
errorLog.write((r.text+"\n").encode('latin-1').decode('unicode_escape'))
print(i)
executingLog.write(str(int(i/14)))
errorLog.close()
executingLog.close()
endTime = datetime.datetime.now()
print("耗时: %d秒, 正确数: %d, 异常数: %d, 总数: %d, 通过率: %.2f%%" % ((endTime-startTime).seconds, rightCount, errCount, i, (rightCount)/i*100)) voiceSearchInterface.exeRequests()
注意: 使用grequests可能有坑, 因为它修改了底层socket通信, 可能会造成系统有问题,我目前虽然还没遇到,但还是在这里友情提醒下.
2. 使用多进程+requests库:
Python里面的多进程库multiprocessing和requests库都是神器, 下面直接上代码:
#_*_coding=utf-8_*_
import multiprocessing
import time
from collections import OrderedDict
import hashlib
import linecache
import os
import requests
import json def formatUrlAndHeader(des, singer, songName):
#生成url和header的逻辑
return url, h #每个进程都去读各自的文件,然后以写文件的方式保存当前的执行记录,为了预防断电或者其他程序异常终止情况
def worker(fileName):
descr = ["播放", "搜索", "搜", "听", "我要听", "我想听", "来一首", "来一个", "来一段", "来一曲", "来首", "来个", "来段", "来曲"]
Logprefix = os.path.split(fileName)[1].replace(".txt", "")
resultLogPath = os.path.join(os.getcwd(), "log", Logprefix+".log")
logbreakPoint = os.path.join(os.getcwd(), "log", Logprefix+".txt")
with open(logbreakPoint, "r") as b:
startLine = int(b.read())
b.close()
with open(resultLogPath, "a+", encoding="utf-8") as logF:
with open(fileName, "r", encoding="utf-8") as f:
lines = f.readlines()
f.close()
LineNum = startLine
for j in range(len(lines)-startLine+1):
LineContent = linecache.getline(fileName, LineNum)
for i in descr:
line = LineContent.split("\t")
singer = line[0]
song = line[1].replace("\n","")
uAndH = formatUrlAndHeader(i, singer, song)
try:
r = requests.get(url=uAndH[0], headers = uAndH[1])
with open(logbreakPoint, "w") as w:
w.write(str(LineNum))
print("searching:%s, line: %d\n" % (fileName, LineNum))
result = json.loads(r.text)
resultSinger = result["data"]["sounds"][0]["singer"]
resultSong = result["data"]["sounds"][0]["title"]
if not (resultSinger==singer and resultSong==song):
logF.write("Error: search des: %s, singer:%s, song:%s;return: %s\n" %(i,singer,song, r.text.encode('latin-1').decode('unicode_escape')))
except Exception as e:
logF.write("Error: search des: %s, singer:%s, song:%s;return: %s\n" %(i,singer,song,str(e).encode('latin-1').decode('unicode_escape')))
LineNum += 1
logF.close() if __name__=='__main__':
orgPath = os.path.join(os.getcwd(), "data")
files = os.listdir(orgPath)
for i in files:
f =os.path.join(orgPath,i)
if os.path.isfile(f):
p = multiprocessing.Process(target=worker, args=(f,))
p.start()
程序会根据数据源文件数量, 生成相应的进程数. 每个进程各自读各自的数据源文件, 然后调用formatUrlAndHeader方法获取url和heade, 挨个发送请求并保存当前执行记录到指定文件. 这种方式的好处在于针对每个请求, 都能对比发送前的参数和收回的请求相应数据.
3. 使用异步asyncio, aiohttp
asyncio是python3.4+才进入的新东西, 是Python3.4+以上的标准库, 是推荐采用的方式, 而aiohttp需要单独安装, 代码如下:
#_*_coding=utf-8_*_
import aiohttp
import time
from collections import OrderedDict
import hashlib
import asyncio
import os
import linecache
import threading def formatUrlAndHeader(des, singer, songName):
#生成url和header的逻辑
return url, h async def fetch_async(uandh):
u, h = uandh[0],uandh[1]
with aiohttp.Timeout(301):
async with aiohttp.request('GET', url=u, headers=h) as r:
data = await r.text()
return data loop = asyncio.get_event_loop()
descr = ["播放", "搜索", "搜", "听", "我要听", "我想听", "来一首", "来一个", "来一段", "来一曲", "来首", "来个", "来段", "来曲"]
orgPath = os.path.join(os.path.dirname(os.getcwd()), "test","SongsAndSingers","singersAndSongs3.txt") def runRequests(startNum):
start = time.time()
urlsAndHs = []
for i in range(20):
line = linecache.getline(orgPath, startNum+i).split("\t")
orgSinger = line[0]
orgSong = line[1].replace("\n","")
for k in descr:
urlAndH = formatUrlAndHeader(k, orgSinger,orgSong)
urlsAndHs.append(urlAndH)
linecache.clearcache()
tasks = [fetch_async(uandh) for uandh in urlsAndHs]
done, pending = loop.run_until_complete(asyncio.wait(tasks))
for i in done:
print(i.result().encode('latin-1').decode('unicode_escape'))
end = time.time()
print(end-start) for i in range(1,50,20):
t = threading.Thread(target=runRequests, args=(i,))
t.start()
t.join()
一个源数据文件, 多线程. 每个线程根据传入的起始行号连续读取文件的20行, 然后批量发送20个请求, 下一个线程必须等待上一个线程结束才开始. 这种方式也是批量发, 批量收回,不能单独对比每个请求的请求前参数, 请求后相应.
以上3种方式, 任何一种都能满足我的测试要求. 实际过程中发现:
1. PHP接口对于单个请求, 参数pagesize对相应速度影响甚大, 具体原因未知;
2. 服务器对IO密集型的操作, 非常消耗CPU. 以上3种方式, 基本上都是每次只发20个请求左右, 而服务器的CPU(8核)已经满载了!
用Python做大批量请求发送的更多相关文章
- 分别用postman和python做post请求接口功能测试
前几天,在做一个post请求的接口功能测试的时候,发现数据始终无法入库, 认真加仔细检查了请求的url.方式.参数,均没有问题 找到技术确认,原来是需要传json格式数据 在头信息中加上类型,body ...
- python通过http请求发送soap报文进行webservice接口调用
最近学习Python调用webservice 接口,开始的时候主要采用suds 的方式生产client调用,后来发现公司的短信接口采用的是soap报文来调用的,然后开始了谷歌,最后采用httplib ...
- python使用post请求发送图片并接受图片
图像读取编码与反编码: import requests import json import numpy as np import cv2 import base64 # 首先将图片读入 # 由于要发 ...
- python用httplib模块发送get和post请求
在python中,模拟http客户端发送get和post请求,主要用httplib模块的功能. 1.python发送GET请求 我在本地建立一个测试环境,test.php的内容就是输出一句话: 1 e ...
- python爬虫---scrapy框架爬取图片,scrapy手动发送请求,发送post请求,提升爬取效率,请求传参(meta),五大核心组件,中间件
# settings 配置 UA USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, l ...
- python 使用 requests 做 http 请求
1. get import requests # 最简单的get请求 r = requests.get(url) print(r.status_code) print(r.json()) # url ...
- Python中http请求方法库汇总
最近在使用python做接口测试,发现python中http请求方法有许多种,今天抽点时间把相关内容整理,分享给大家,具体内容如下所示: 一.python自带库----urllib2 python自带 ...
- python接口测试—get请求(一)
python 做借口测试用到的是requests模块,首先要导入requests库,pip install requests 1.get直接请求方式 以豆瓣网为例: url = 'https://re ...
- python的post请求抓取数据
python通过get方式,post方式发送http请求和接收http响应-urllib urllib2 python通过get方式,post方式发送http请求和接收http响应-- import ...
随机推荐
- Linux 最新SO_REUSEPORT特性
1.前言 昨天总结了一下Linux下网络编程“惊群”现象,给出Nginx处理惊群的方法,使用互斥锁.为例发挥多核的优势,目前常见的网络编程模型就是多进程或多线程,根据accpet的位置,分为如下场景: ...
- HTML5+CSS3静态页面项目-PayPaul的总结
学习前端有一段时间了,一直在看书上的理论知识,而实战项目却很少.师兄常说,想要知道自己的实力有多少,知识掌握了多少,最好的方法就是去实践了,实践出真知嘛.于是决定在这个假期里,主要是通过项目的实践以及 ...
- Vuejs技术栈从CLI到打包上线实战全解析
前言 本文是自己vue项目实践中的一些总结,针对Vue2及相关技术栈,实践中版本为2.3.3. 开发前须知 vue-cli 在开发前,我们要至少通读一遍vue官方文档和API(看官方文档是最重要的,胜 ...
- [命令行] curl查询公网出口IP
转载:http://blog.csdn.net/orangleliu/article/details/51994513 不管是在家里还是办公室,或者是公司的主机,很多时候都是在内网中,也就是说很多都是 ...
- 9.如何解决出现AXIOS的Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
问题描述: 由于restful接口需要在头部header传递两个字段: Content-Type: application/jsonAccess-Token: 84c6635800b14e0eba4f ...
- Redis初体验
简介 Redis是一个速度非常快的非关系型数据库,它不仅性能强劲,而且还具有复制特性以及为解决问题而生的独一无二的数据模型.作为键值型数据库,Redis支持5中数据类型:字符串,列表,集 ...
- Linux系统安全配置基线
一:共享账号检查 配置名称:用户账号分配检查,避免共享账号存在 配置要求:1.系统需按照实际用户分配账号: 2.避免不同用户间共享账号,避免用户账号和服务器间通信使用的账号共享. 操作指南:参考配置操 ...
- 2017寒假零基础学习Python系列之函数之 编写函数
定义一个函数用def语句 格式为:def + 函数名.括号.括号中的参数和冒号 比如定义一个求绝对值的函数: def my_abs(x): if x>= 0: return x else ret ...
- STL—vector空间的动态增长
vector空间的动态增长 当添加元素时,如果vector空间大小不足,则会以原大小的两倍另外配置一块较大的新空间,然后将原空间内容拷贝过来,在新空间的内容末尾添加元素,并释放原空间.vect ...
- GitHub 入门教程
一.前言 编程进阶的道路是坎坷的,没有任何捷径.这个时期只能是积累.吸收.学习.坚持,做到量的积累,到质的飞跃 古语有云:'书山有路,勤为径'.'不积跬步,无以至千里' 编程是一个动手实践性的学科,多 ...