通过python异步通讯方式构建高并发压力测试工具
背景说明
在工作中,要对一个接口进行压测,我当时就想通过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 |
2 |
开始时间: 1536546027.6947124 |
3 |
开始时间: 1536546205.2600951 |
4 |
开始时间: 1536546368.2361982 |
5 |
开始时间: 1536546640.4913867 |
运行时间很长,对程序进行了分析,因为循环是单线程并且是同步的,发送请求后,必须等待收到响应,才会发送下一个请求,效率很低,并且循环对压力机的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 |
开始时间: 1536546926.9662883 |
2 |
开始时间: 1536546027.6947124 |
开始时间: 1536546962.368912 |
3 |
开始时间: 1536546205.2600951 |
开始时间: 1536546990.911677 |
4 |
开始时间: 1536546368.2361982 |
开始时间: 1536547021.2030604 |
5 |
开始时间: 1536546640.4913867 |
开始时间: 1536547046.3298163 |
测试效率有很大提高,但也存在问题,当启动线程较多时,压力机资源消耗大,在同一个线程内部,还是同步进行,效率较低。
异步通讯方案
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 |
开始时间: 1536546926.9662883 |
开始时间: 1536565502.732898 |
2 |
开始时间: 1536546027.6947124 |
开始时间: 1536546962.368912 |
开始时间: 1536565502.732898 |
3 |
开始时间: 1536546205.2600951 |
开始时间: 1536546990.911677 |
开始时间: 1536565502.732898 |
4 |
开始时间: 1536546368.2361982 |
开始时间: 1536547021.2030604 |
开始时间: 1536565502.732898 |
5 |
开始时间: 1536546640.4913867 |
开始时间: 1536547046.3298163 |
开始时间: 1536565502.732898 |
性能大大提高,但还有一个需要优化的地方,tasks采用的是list,如果数量多了,会占用大量内存,下步进行优化。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步