1. 需求背景

测出当前测试环境下单推和多推性能相关指标

2. 术语解释

性能测试定义:指通过自动化测试工具模拟多种正常,峰值以及负载条件对系统的各项性能指标进行测试

响应时间:用户发送一个请求到服务器返回的响应数据这段时间就是响应时间。

并发用户数:某一物理时刻同时向系统提交请求的用户数,提交的请求可能是同一个场景或功能,也可以是不同场景或功能。

TPS: 全拼是Transaction per second,每秒事务数,一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。

单推: 下发一条消息只推送一个用户id (多个用户收到消息是通过推送多条消息来实现的)

多推:一条推送消息可以同时推送多个用户id。(多个用户收到消息是通过一条消息来实现的)

3. 测试策略

3.1 需求分析:

针对需求,现需测试下列几个场景:

  • 使用域名单推,查看发送的最大TPS

  • 使用域名多推,查看发送的最大TPS

3.2 测试方法

1. 性能测试的前期准备 
  • 分析业务场景 场景内容有哪些,范围较广,可与开发、产品,讨论确定本次测试的范围

  • 分析测试策略 得到设计的测试场景有哪些

  • 分析生产环境

  • 选择测试工具 用什么方式来测试性能

    2. 性能测试的目的  
  • 性能测试则通过提前模拟场景压力,来发现系统中可能的瓶颈,提前修复这些bug,减少服务器宕机的风险。

  • 性能测试还可以用来评估待测软件在不同负载下的运作状况,可以针对某些数据得到一些决策

    3.性能下降曲线分析法
  • 性能随用户数增长而出现下降趋势的曲线

  • 性能主要指响应时间

  • 分为:单用户区域、性能平坦区、压力区域、性能拐点

3.3 测试工具

  • 使用分布式压测工具locust

3.4 测试环境

3.4.1 架构图

null

3.4.2 被测系统

  • API服务为双机双节点部署方式,相关依赖服务部署也都部署在这台服务起上,且都是双节点
  • 服务器资源:4核16G
  • 使用域名压测
  • 域名通过lbs转发到服务器,转发方式:负载均衡(加权轮询,两边权重一样)

4 测试工具Locust

  • 4.1 简要

JMeter和LoadRunner性能测试工具,是比较老牌的,都是通过线程来作为虚拟虚拟用户,而Locust是基于协程实现并发用户的,协程是比线程更小的单位,也称为子线程,在一个线程中可以运行多个协程。所以基于协程的Locust,在进行分布式部署时,不仅可以进行多机压测部署,而且还可以在一台宿主机中完成,

  • 4.2 压测工具:locust,版本:0.14.5

  • 4.3 Locust本地宿主机部署步骤

    1.首先我们需要通过python+locust写出程序需要运行的ABC.py文件

# coding=utf-8  
# python3.6  
from locust import HttpLocust,TaskSet,task,between  
import requests, base64  
import datetime  
import json,time  
import string,random  
import urllib3.request  
from requests_toolbelt.utils import dump  
import hashlib  

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)  


def Android_push_api(self,url,firmId,fiBasicrmSecret,data):  
    data = json.dumps(data)  
    base64header = base64.b64encode((firmId + ":" + fiBasicrmSecret).encode(encoding='utf-8'))  
    headers = {  
            "Content-Type": "application/json",  
        "Authorization": "Basic %s" % base64header  
        }  
    response = self.client.post(url=url,headers=headers,data=data,verify=False)  
    request_headers = response.request.headers  
    data = dump.dump_all(response)  
    status_code = response.status_code  
    resp_headers = response.headers  
    return response.text  

#定义任务类  
class TuibidaPush(TaskSet):  
    firmId = "********" #是推必达分配给厂商  
    firmSecret = "*******************"  #是推必达分配给厂商  

    def on_start(self):      
        self.deviceToken = hashlib.md5("*****".encode(encoding='utf-8')).hexdigest()  

        @task(1) #申明需要执行的任务  
        def tuipush(self):  

             typelist = [1,2]  
             msgType = random.choice(typelist)  
                if msgType == 1:  
                body={"111":self.11111,"msgType":1}  
              else:  
                body={"111":self.11111,"msgType":2,"msg":{"333 ":"333","444":4444}}  
                rsp=Android_push_api(self,'/v2/push/single_device', self.firmId,self.firmSecret,body)  


#执行任务类  
class websitUser(HttpLocust):  
      task_set = TuibidaPush #定义需要执行的任务集  
      wait_time = between(0.010, 0.100)  
      #min_wait = 1000 #最小等待时间(思考时间)  
      #max_wait = 2000 #最大等待时间(思考时间)  

2.创建locust运行的master:

写好需要运行的locust文件之后,我们需要先利用命令“locust -f xxxx.py --master”创建一个master,这个master不参与创建并发用户的工作,它主要是进行监听以及收集统计数据.

2.1 提供给web端,我们完全可以利用python当中的os的system函数直接编写出cmd命令进行运行

if __name__ == "__main__":  
     #开启master模式  
     import os  
     os.system("locust -f ABC.py --host=http://ip:port --master")  

2.2 使用no-web,也可以利用python当中的os的system函数直接编写出cmd命令进行运行

    if __name__ == "__main__":  
     #开启master模式  
     import os  
     os.system("locust -f ABC.py --host=http://域名 --master --no-web -c 50 -r 5 -t 1m" )  

当然也可以直接输入命令执行

     locust -f ABC.py --host=http://域名 --master --no-web -c 50 -r 5 -t 1m  

此时我们运行py文件,可以看到以下的信息,则说明我们已经启动了locust分布式的主节点master

[2020-03-26 14:53:01,627] bjdz-qa-jpush-hostname****/INFO/locust.main: Starting web monitor at http://*:8089  
[2020-03-26 14:53:01,628] bjdz-qa-jpush-hostname****/INFO/locust.main: Starting Locust 0.14.5  

3.创建locust运行的slave

我们就可以创建用于制造并发用户的分支节点slave了,方法就是复制master的文件,然后修改一下文件名称,一定要保证slave运行的文件与master文件是一样的,自己的机器有多少个cpu就可以弄多少个slave,一般一个slave会使用一个cpu创建并发用户

 ABC.py  
 ABC_slave1.py  

3.1 然后每个slave文件中需要在主函数中输入cmd命令

if __name__=="__main__":  
    import os  
    os.system("locust -f ABC_slave1.py --slave --master-host=ip")  

3.2 也可直接输入命令执行slave

locust -f ABC_slave1.py --slave --master-host=ip  

运行多个slave,可以查看以下内容来判断分布式是否部署成功

[2020-03-17 13:45:32,422] DESKTOP-LB6NBMD/INFO/locust.main: Starting Locust 0.9.0  

当开启全部slave后,可以查看master的控制台信息,看到下面的信息则表示locust分布式部署成功了

[2020-03-26 14:53:01,627] bjdz-qa-jpush-hostname****/INFO/locust.main: Starting web monitor at http://*:8089  
[2020-03-26 14:53:01,628] bjdz-qa-jpush-hostname****/INFO/locust.main: Starting Locust 0.14.5  
[2020-03-26 14:53:01,757] bjdz-qa-jpush-hostname****/INFO/locust.runners: Client 'bjdz-qa-jpush-hostname****_182786b913fc4b58b5a262792dcef5d8' reported as ready. Currently 1 clients ready to swarm.  
[2020-03-26 14:54:12,676] bjdz-qa-jpush-hostname****/INFO/locust.runners: Client 'bjdz-qa-jpush-hostname****_9078b2f6ec0440a79645af801560f60b' reported as ready. Currently 2 clients ready to swarm.  
[2020-03-26 14:55:24,089] bjdz-qa-jpush-hostname****/INFO/locust.runners: Client 'bjdz-qa-jpush-hostname****_2415fe92abfe49d68d33022ed8648089' reported as ready. Currently 3 clients ready to swarm.  
[2020-03-26 14:55:32,758] bjdz-qa-jpush-hostname****/INFO/locust.runners: Client 'bjdz-qa-jpush-hostname****_1efae38dd2d1403692454e03e1dfbffe' reported as ready. Currently 4 clients ready to swarm.  
[2020-03-26 14:55:42,398] bjdz-qa-jpush-hostname****/INFO/locust.runners: Client 'bjdz-qa-jpush-hostname****_ab1dc9292a064fceb832b0e077b80ef3' reported as ready. Currently 5 clients ready to swarm.  
[2020-03-26 14:55:50,440] bjdz-qa-jpush-hostname****/INFO/locust.runners: Client 'bjdz-qa-jpush-hostname****_91511457dfe14de7b43f3be6e852b24e' reported as ready. Currently 6 clients ready to swarm.  
[2020-03-26 14:56:00,376] bjdz-qa-jpush-hostname****/INFO/locust.runners: Client 'bjdz-qa-jpush-hostname****_d465c7709f7042b6b5f05b52fef4b2c4' reported as ready. Currently 7 clients ready to swarm.  
[2020-03-26 14:56:22,224] bjdz-qa-jpush-hostname****/INFO/locust.runners: Client 'bjdz-qa-jpush-hostname****_b6ce87847df748729f738e51a2edc293' reported as ready. Currently 8 clients ready to swarm.  

4、打开web端同样也可以查看到分布式的slave

登陆页面

null

查看slave节点数
null

测试结果数据下载
null

在locust当中使用web模式进行压测时是没有办法设定执行时间的,所以web模式下的压测会一直运行,直到点击stop才能运行,如果想在web模式下运行压测一段时间自动停止的话,那么需要在参数配置时加入stop_timeout参数即可,value值是执行的时间(单位是s)

class websitUser(HttpLocust):  
    task_set = Login #定义需要执行的任务集  
    min_wait = 1000 #最小等待时间(思考时间)  
    max_wait = 2000 #最大等待时间(思考时间)  
    stop_timeout = 60  

5 数据采集

  • 单推接口:/v2/push/single_device
线程数 并发用户数 TPS CPU 使用率 内存使用率 备注
5 200 2156/s 87% 65% 无请求间隔
3 200 2178/s 87% 65% 无请求间隔
3 150 1656/s 77% 65% 无请求间隔
3 170 1800/s 83% 65% 无请求间隔
3 160 1757/s 80% 65% 无请求间隔
10 160 1700/s 79% 65% 无请求间隔
1 160 1728/s 79% 65% 无请求间隔
10 1000 45200/s 99% 66% 无请求间隔
  • 多推接口:/v2/push/device_list
    线程数 并发用户数 TPS CPU 使用率 内存使用率 备注
    3 150 1645/s 83% 65% 无请求间隔
    2 120 1330/s 78% 62% 无请求间隔
    3 120 1331/s 80% 64% 无请求间隔
    10 120 1332/s 82% 64% 无请求间隔

6. 数据分析

压测时内存消耗没有cpu消耗明显。从系统稳定性考虑,需要考虑服务不挂,稳定情况下的性能指标。需要对数据采集进行分析,分析得如下结论:

单推:线程为3,并发用户数为160,cpu消耗在80%左右来持续压测得出下面的性能各项指标。

多推:线程为2,并发用户数为120,cpu消耗在78%左右来持续压测得出下面的性能各项指标。

注:多推时,考虑到body的大小,deviceTokens设置3个数,对接口来说无差异,在数据落地时才消耗资源,另外借助单推的经验和多推的系统逻辑,简单可以定位出多推的资源消耗。所以数据采集相对少些。)

7.测试结论

提供接口 请求方式 线程数 CPU 使用率 内存使用率 请求总数 错误数 中间响应时间
(ms)
平均响应时间
(ms)
最低响应时间
(ms)
最长响应时间
(ms)
请求数据大小
(bytes)
90%响应时间
(ms)
tps/s 备注
/v2/push/single_device post 3 80% 64% 6405714 0 33 34 27 1123 44 36 1757 无请求间隔
/v2/push/device_list post 2 78% 63% 3343954 0 33 34 28 315 44 35 1342 无请求间隔

单推接口:/v2/push/single_device 接口TPS压测结果为 1757/s,90%请求可以在 0.1 秒内响应,因线上 部署多台机器,所以该 tps 只限当前配置和部署的情况下,主要瓶颈是设备升级;

多推接口:/v2/push/device_list 接口TPS压测结果为 1342/s,90%请求可以在 0.1 秒内响应,因线上部 署多台机器,所以该 tps 只限当前配置和部署的情况下,主要瓶颈是设备升级;