背景说明
在工作中,要对一个接口进行压测,我当时就想通过python自己编写一个压力发生器。
初步方案(单线程循环发送)
通过循环向服务端发送请求,代码如下:
#采用单步循环的方式循环测试
import requests,time
def run(runnum):
url = "https://api-test.peanut.ai/wechatGrant/load/test1?openId=RP0ulQ4pHDTBWt77ILCs02QGU&bsscode=8167871547864571"
for i in range(runnum):
str_res = requests.get(url)
if __name__ == "__main__":
start_time = time.time()
run(100)
end_time = time.time()
#print("循环次数:",str(counut))
print("开始时间:",str(start_time))
print("结束时间:",str(end_time))
print("运行时间:",str(end_time - start_time))
测试结果如下:
单线程
1
开始时间: 1536545804.5258229
结束时间: 1536545879.1070435
运行时间: 74.58122062683105
2
开始时间: 1536546027.6947124
结束时间: 1536546100.3893104
运行时间: 72.69459795951843
3
开始时间: 1536546205.2600951
结束时间: 1536546270.549498
运行时间: 65.28940296173096
4
开始时间: 1536546368.2361982
结束时间: 1536546435.7475684
运行时间: 67.51137018203735
5
开始时间: 1536546640.4913867
结束时间: 1536546712.5064435
运行时间: 72.015056848526
运行时间很长,对程序进行了分析,因为循环是单线程并且是同步的,发送请求后,必须等待收到响应,才会发送下一个请求,效率很低,并且循环对压力机的CPU资源消耗较大。
多线程方案
考虑通过多线程提高测试效率,代码如下:
import threading
import requests
import time
url = "https://api-test.peanut.ai/wechatGrant/load/test1?openId=RP0ulQ4pHDTBWt77ILCs02QGU&bsscode=8167871547864571"
def run_thread(snum,enum):
for i in range(snum,enum):
#s = requests.session()
#关闭长连接
headers = {'Connection': 'close'}
str_res = requests.get(url,headers=headers)
#从发送请求到收到响应消耗的时间,单位微妙。
etime = str_res.elapsed.microseconds/1000000
threads = []
for i in range(0,10):
#循环生成线程
t = threading.Thread(target=run_thread, args=(0,10))
threads.append(t)
if __name__ == "__main__":
start_time = time.time()
for dd in range(0,10):
#启动线程
threads[dd].start()
for dd in range(0,10):
threads[dd].join()
end_time = time.time()
print("开始时间:",str(start_time))
print("结束时间:",str(end_time))
print("运行时间:",str(end_time - start_time))
测试结果如下:
单线程
多线程(10)
1
开始时间: 1536545804.5258229
结束时间: 1536545879.1070435
运行时间: 74.58122062683105
开始时间: 1536546926.9662883
结束时间: 1536546935.9247773
运行时间: 8.958488941192627
2
开始时间: 1536546027.6947124
结束时间: 1536546100.3893104
运行时间: 72.69459795951843
开始时间: 1536546962.368912
结束时间: 1536546970.9876196
运行时间: 8.618707656860352
3
开始时间: 1536546205.2600951
结束时间: 1536546270.549498
运行时间: 65.28940296173096
开始时间: 1536546990.911677
结束时间: 1536547001.0316806
运行时间: 10.120003700256348
4
开始时间: 1536546368.2361982
结束时间: 1536546435.7475684
运行时间: 67.51137018203735
开始时间: 1536547021.2030604
结束时间: 1536547030.7051418
运行时间: 9.502081394195557
5
开始时间: 1536546640.4913867
结束时间: 1536546712.5064435
运行时间: 72.015056848526
开始时间: 1536547046.3298163
结束时间: 1536547054.6956365
运行时间: 8.365820169448853
测试效率有很大提高,但也存在问题,当启动线程较多时,压力机资源消耗大,在同一个线程内部,还是同步进行,效率较低。
异步通讯方案
asyncio可以实现单线程并发IO操作。如果仅用在客户端,发挥的威力不大。如果把asyncio用在服务器端,例如Web服务器,由于HTTP连接就是IO操作,因此可以用单线程+coroutine实现多用户的高并发支持。
#采用异步通讯的方式发压
import requests,time
import asyncio
from aiohttp import ClientSession
import aiohttp
url = "https://api-test.peanut.ai/wechatGrant/load/test1?openId=RP0ulQ4pHDTBWt77ILCs02QGU&bsscode=8167871547864571"
tasks = []
async def run():
async with ClientSession() as session:
async with session.get(url) as response:
response = await response.read()
#print(response)
if __name__ == "__main__":
loop = asyncio.get_event_loop()
start_time = time.time()
for i in range(100):
tasks.append(run())
end_time = time.time()
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
#print("循环次数:",str(counut))
print("开始时间:",str(start_time))
print("结束时间:",str(end_time))
print("运行时间:",str(end_time - start_time))
测试结果:
单线程
多线程(10)
异步
1
开始时间: 1536545804.5258229
结束时间: 1536545879.1070435
运行时间: 74.58122062683105
开始时间: 1536546926.9662883
结束时间: 1536546935.9247773
运行时间: 8.958488941192627
开始时间: 1536565502.732898
结束时间: 1536565502.732898
运行时间: 0.0
2
开始时间: 1536546027.6947124
结束时间: 1536546100.3893104
运行时间: 72.69459795951843
开始时间: 1536546962.368912
结束时间: 1536546970.9876196
运行时间: 8.618707656860352
开始时间: 1536565502.732898
结束时间: 1536565502.732898
运行时间: 0.0
3
开始时间: 1536546205.2600951
结束时间: 1536546270.549498
运行时间: 65.28940296173096
开始时间: 1536546990.911677
结束时间: 1536547001.0316806
运行时间: 10.120003700256348
开始时间: 1536565502.732898
结束时间: 1536565502.732898
运行时间: 0.0
4
开始时间: 1536546368.2361982
结束时间: 1536546435.7475684
运行时间: 67.51137018203735
开始时间: 1536547021.2030604
结束时间: 1536547030.7051418
运行时间: 9.502081394195557
开始时间: 1536565502.732898
结束时间: 1536565502.732898
运行时间: 0.0
5
开始时间: 1536546640.4913867
结束时间: 1536546712.5064435
运行时间: 72.015056848526
开始时间: 1536547046.3298163
结束时间: 1536547054.6956365
运行时间: 8.365820169448853
开始时间: 1536565502.732898
结束时间: 1536565502.732898
运行时间: 0.0
性能大大提高,但还有一个需要优化的地方,tasks采用的是list,如果数量多了,会占用大量内存,下步进行优化。