## 12) Lars Agent性能測試工具
### 12.1 QPS測試
> Lars/api/cpp/example/qps.cpp
```c
#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服務
看客戶端運行結果
```bash
$ ./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
```c
#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
```c
#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
```c
#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
```c
#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](mailto:danbing.at@gmail.com)
github: [https://github.com/aceld](https://github.com/aceld)
原創書籍: [http://www.hmoore.net/@aceld](http://www.hmoore.net/@aceld)

>**原創聲明:未經作者允許請勿轉載, 如果轉載請注明出處**
- 一、Lars系統概述
- 第1章-概述
- 第2章-項目目錄構建
- 二、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啟動工具腳本