通过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
结束时间: 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,如果数量多了,会占用大量内存,下步进行优化。

原文地址:https://www.cnblogs.com/devtest/p/9620811.html