(38) Lars Agent性能测试工具(LoadBalanceAgent部分)-【Lars-基于C++负载均衡远程服务器调度系统教程】

【Lars教程目录】

Lars源代码
https://github.com/aceld/Lars


【Lars系统概述】
第1章-概述
第2章-项目目录构建


【Lars系统之Reactor模型服务器框架模块】
第1章-项目结构与V0.1雏形
第2章-内存管理与Buffer封装
第3章-事件触发EventLoop
第4章-链接与消息封装
第5章-Client客户端模型
第6章-连接管理及限制
第7章-消息业务路由分发机制
第8章-链接创建/销毁Hook机制
第9章-消息任务队列与线程池
第10章-配置文件读写功能
第11章-udp服务与客户端
第12章-数据传输协议protocol buffer
第13章-QPS性能测试
第14章-异步消息任务机制
第15章-链接属性设置功能


【Lars系统之DNSService模块】
第1章-Lars-dns简介
第2章-数据库创建
第3章-项目目录结构及环境构建
第4章-Route结构的定义
第5章-获取Route信息
第6章-Route订阅模式
第7章-Backend Thread实时监控


【Lars系统之Report Service模块】
第1章-项目概述-数据表及proto3协议定义
第2章-获取report上报数据
第3章-存储线程池及消息队列


【Lars系统之LoadBalance Agent模块】
第1章-项目概述及构建
第2章-主模块业务结构搭建
第3章-Report与Dns Client设计与实现
第4章-负载均衡模块基础设计
第5章-负载均衡获取Host主机信息API
第6章-负载均衡上报Host主机信息API
第7章-过期窗口清理与过载超时(V0.5)
第8章-定期拉取最新路由信息(V0.6)
第9章-负载均衡获取Route信息API(0.7)
第10章-API初始化接口(V0.8)
第11章-Lars Agent性能测试工具
第12章- Lars启动工具脚本


12) Lars Agent性能测试工具

12.1 QPS测试

Lars/api/cpp/example/qps.cpp

#include <stdlib.h>
#include <iostream>
#include <pthread.h>
#include "lars_api.h"

struct ID
{
    int t_id;
    int modid;
    int cmdid;
};


void *test_qps(void *args)
{
    int ret = 0;

    ID *id = (ID*)args;

    int modid = id->modid;
    int cmdid = id->cmdid;

    lars_client api;

    std::string ip;
    int port;

    //qps记录
    long qps = 0;

    //记录最后时间
    long last_time = time(NULL);

    long total_qps = 0;
    long total_time_second = 0;

    //1. lars_api 初始化(只调用一次)
    ret = api.reg_init(modid, cmdid);
    if (ret != 0) {
        std::cout << "modid " << modid << ", cmdid " << cmdid << " still not exist host, after register, ret = " << ret << std::endl;
    }
    
    while (1) {
        ret = api.get_host(modid, cmdid, ip, port);
        if (ret == 0 || ret == 1 || ret == 3) { // 成功,过载,不存在 均是合法返回
            ++qps;
            if (ret == 0) {
                api.report(modid, cmdid, ip, port, 0);//上报成功
            }
            else if (ret == 1) {
                api.report(modid, cmdid, ip, port, 1);//上报过载
            }
        }
        else {
            printf("[%d,%d] get error %d\n", modid, cmdid, ret);
        }

        //当前时间
        long current_time = time(NULL);

        if (current_time - last_time >= 1) {
            total_time_second += 1;
            total_qps += qps;
            last_time = current_time;
            printf("thread:[%d] --> qps = [%ld], average = [%ld]\n", id->t_id, qps, total_qps/total_time_second);
            qps = 0;
        }
    }

    return NULL;
}

int main(int argc, char **argv)
{
    if (argc != 2) {
        printf("./qps [thread_num]\n");
        return 1;
    }

    int cnt = atoi(argv[1]);

    ID *ids = new ID[cnt];
    pthread_t *tids = new pthread_t[cnt];

    //制作模拟的modid/cmdid
    for (int i = 0; i < cnt; i++) {
        ids[i].t_id = i;
        ids[i].modid = i + 1;
        ids[i].cmdid = 1;
    }

    for (int i = 0; i < cnt; i++) {
        pthread_create(&tids[i], NULL, test_qps, &ids[i]);
    }

    for (int i = 0; i < cnt; i++) {
        pthread_join(tids[i], NULL);
    }

    return 0;
}

12.2 测试结果

​ 分别启动Lars-Reporter、Lars-Dns、Lars-LB-Agent三个service服务

看客户端运行结果

$ ./qps 1
thread:[0] --> qps = [4594], average = [4594]
thread:[0] --> qps = [5159], average = [4876]
thread:[0] --> qps = [5190], average = [4981]
thread:[0] --> qps = [5039], average = [4995]
thread:[0] --> qps = [4949], average = [4986]
thread:[0] --> qps = [5111], average = [5007]
thread:[0] --> qps = [5075], average = [5016]
thread:[0] --> qps = [5067], average = [5023]
thread:[0] --> qps = [5104], average = [5032]
thread:[0] --> qps = [4976], average = [5026]
thread:[0] --> qps = [5022], average = [5026]
thread:[0] --> qps = [5088], average = [5031]
thread:[0] --> qps = [5114], average = [5037]
thread:[0] --> qps = [5072], average = [5040]
thread:[0] --> qps = [4808], average = [5024]
thread:[0] --> qps = [5119], average = [5030]
thread:[0] --> qps = [5055], average = [5031]
thread:[0] --> qps = [5026], average = [5031]
thread:[0] --> qps = [5040], average = [5032]
thread:[0] --> qps = [4931], average = [5026]
thread:[0] --> qps = [5073], average = [5029]
...

​ 这里我们客户端是开启一个线程进行测试,平均每秒服务端会响应5000次左右。

​ 这里我简单用两个主机,分别测试了一些数据

主机1:

CPU个数:2个 , 内存: 2GB , 系统:Ubuntu18.04虚拟机

线程数 QPS
1 0.5w/s
2 2.2w/s
10 5.5w/s
100 5.3w/s

主机2:

CPU个数: 24个 , 内存:128GB, 系统: 云主机

线程数 QPS
1 8.36w/s
3 28.06w/s
5 55.18w/s
8 56.74w/s

12.2 Lars模拟器系统测试

Lars/api/cpp/example/simulator.cpp

#include "lars_api.h"
#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <map>

void usage()
{
    printf("usage: ./simulator ModID CmdID [errRate(0-10)] [query cnt(0-999999)]\n");
}

int main(int argc, char **argv)
{
    int ret = 0;

    if (argc < 3) {
        usage();
        return 1;
    }

    int modid = atoi(argv[1]);
    int cmdid = atoi(argv[2]);
    int err_rate = 2;
    int query_cnt = 100;

    if (argc > 3) {
        err_rate = atoi(argv[3]);
    }

    if (argc > 4) {
        query_cnt = atoi(argv[4]);
    }

    lars_client api;

    std::string ip;
    int port;
    //key---ip,  value---<succ_cnt, err_cnt>
    std::map<std::string, std::pair<int, int>> result;

    std::cout << "err_rate = " << err_rate << std::endl;

    //1. lars_api 初始化(只调用一次)
    ret = api.reg_init(modid, cmdid);
    if (ret != 0) {
        std::cout << "modid " << modid << ", cmdid " << cmdid << " still not exist host, after register, ret = " << ret << std::endl;
    }

    srand(time(NULL));

    for (int i = 0; i < query_cnt; i++) {
        ret = api.get_host(modid, cmdid, ip, port);
        if (ret == 0) {
            //获取成功
            if (result.find(ip) == result.end()) {
                // 首次获取当前ip
                std::pair<int ,int> succ_err(0, 0);
                result[ip] = succ_err;
            }

            std::cout << "host " << ip << ":" << "host" << "called ";
            if (rand()%10 < err_rate) {// 80%的几率产生调用失败
                result[ip].second += 1;
                api.report(modid, cmdid, ip, port, 1);
                std::cout << " ERROR!!!" << std::endl;
            }
            else {
                result[ip].first += 1;
                api.report(modid, cmdid, ip, port, 0);
                std::cout << " SUCCESS." << std::endl;
            }
        }
        else if (ret == 3) {
            std::cout << modid << "," << cmdid << " not exist" << std::endl;
        }
        else if (ret == 2) {
            std::cout << "system err" << std::endl;
        }
        else if (ret == 1) {
            std::cout << modid << "," << cmdid << " all hosts were overloaded!!!" << std::endl;
        }
        else {
            std::cout << "get error code " << ret << std::endl;
        }
        usleep(6000);
    }

    
    //遍历结果
    std::map<std::string, std::pair<int, int>>::iterator it;
    for (it = result.begin(); it != result.end(); it ++) {
        std::cout <<"ip : " << it->first << ": ";
        std::cout <<"success: " << it->second.first << "; ";
        std::cout <<"error: " << it->second.second << std::endl;
    }
    
    return 0;
}

12.3 get_host测试工具

Lars/api/cpp/example/get_host.cpp

#include "lars_api.h"
#include <iostream>

void usage()
{
    printf("usage: ./get_host [modid] [cmdid]\n");
}

int main(int argc, char **argv)
{
    int ret = 0;

    if (argc != 3) {
        usage();
        return 1;
    }

    int modid = atoi(argv[1]);
    int cmdid = atoi(argv[2]);
    lars_client api;

    std::string ip; 
    int port;


    //1. lars_api 初始化(只调用一次)
    ret = api.reg_init(modid, cmdid);
    if (ret != 0) {
        std::cout << "modid " << modid << ", cmdid " << cmdid << " still not exist host, after register, ret = " << ret << std::endl;
    }

    //2. 获取一个host的ip+port
    ret = api.get_host(modid, cmdid, ip, port);
    if (ret == 0) {
        std::cout << "host is " << ip << ":" << port << std::endl;
    }

    return 0;
}

12.4 get_route测试工具

Lars/api/cpp/example/get_route.cpp

#include "lars_api.h"
#include <iostream>

void usage()
{
    printf("usage: ./get_route [modid] [cmdid]\n");
}

int main(int argc, char **argv)
{
    int ret = 0;

    if (argc != 3) {
        usage();
        return 1;
    }

    int modid = atoi(argv[1]);
    int cmdid = atoi(argv[2]);
    lars_client api;

    //1. lars_api 初始化(只调用一次)
    ret = api.reg_init(modid, cmdid);
    if (ret != 0) {
        std::cout << "modid " << modid << ", cmdid " << cmdid << " still not exist host, after register, ret = " << ret << std::endl;
    }

    //2. 获取modid/cmdid下全部的host的ip+port
    route_set route;
    ret = api.get_route(modid, cmdid, route);
    if (ret == 0) {
        for (route_set_it it = route.begin(); it != route.end(); it++) {
            std::cout << "ip = " << (*it).first << ", port = " << (*it).second << std::endl;
        }
    }

    return 0;
}

12.5 reporter测试工具

Lars/api/cpp/example/report.cpp

#include "lars_api.h"
#include <iostream>

void usage()
{
    printf("usage: ./report ModID CmdID IP Port 0|1 --- 0:succ, 1:overload \n");
}

int main(int argc, char **argv)
{
    int ret = 0;

    if (argc != 6) {
        usage();
        return 1;
    }

    int modid = atoi(argv[1]);
    int cmdid = atoi(argv[2]);
    std::string ip = argv[3]; 
    int port = atoi(argv[4]);
    int ret_code = atoi(argv[5]);

    lars_client api;

    //1. lars_api 初始化(只调用一次)
    ret = api.reg_init(modid, cmdid);
    if (ret != 0) {
        std::cout << "modid " << modid << ", cmdid " << cmdid << " still not exist host, after register, ret = " << ret << std::endl;
    }

    api.report(modid, cmdid, ip, port, ret_code);

    std::string result = (ret_code == 0)? "SUCC" :"OVERLOAD";

    std::cout << "report modid = " << modid << ", cmdid = " << cmdid << " | " <<  ip << ":" << port << " " << result << std::endl;

    return 0;
}

FAQ

init_succ = 180,err_rate = 0.1
而 : 10succ+21err 才过载,实际失败率并不是0.1啊?

答:

实际上,观察一组数据:

10succ+21err 过载 实际rate=67%
20succ+22err 过载 实际rate=50%
......
90succ + 30err 过载 实际rate=25%
......
200succ + 42err 过载,实际rate=17%
......
500succ + 75err 过载,实际rate=13%
即量越大越接近我们设定的=10%

而量越小,失败率更大才会导致过载

这样的设计是很好的,因为量小的时候不应该武断的认为10%就过载,比如10succ+2err就会过载,才失败了两次,但是达到了10%,所以过载了,这是很不合理的

我们的init_succ弄巧成拙的在这方面防止了这个情况(本来只想用他防止一上来就失败过载的情况)


关于作者:

作者:Aceld(刘丹冰)

mail: danbing.at@gmail.com
github: https://github.com/aceld
原创书籍gitbook: http://legacy.gitbook.com/@aceld

原创声明:未经作者允许请勿转载, 如果转载请注明出处