## 14) Reactor框架QPS性能測試
? 接下來我們寫一個測試用例來測一下我們的Reactor框架的qps。
> qps: (Query Per Second)每秒查詢率QPS是對一個特定的查詢服務器在規定時間內所處理流量多少的衡量標準。
### 14.1 測試用例代碼編寫
? 我們首先定義一個proto文件,用來承載客戶端和服務端通信媒介的。
> example/qps_test/echoMessage.proto
```protobuf
syntax = "proto3";
package qps_test;
message EchoMessage
{
string content = 1;
int32 id = 2;
};
```
? 然后生成對應的cc文件和h文件
```bash
protoc --cpp_out=. ./*.proto
```
?
? 接下來我們來實現服務端,服務端主要就是簡單的回顯,客戶端發什么數據,回顯就可以了。
> example/qps_test/server.cpp
```c
#include <string>
#include <string.h>
#include "config_file.h"
#include "tcp_server.h"
#include "echoMessage.pb.h"
//回顯業務的回調函數
void callback_busi(const char *data, uint32_t len, int msgid, net_connection *conn, void *user_data)
{
qps_test::EchoMessage request, response;
//解包,確保data[0-len]是一個完整包
request.ParseFromArray(data, len);
//設置新pb包
response.set_id(request.id());
response.set_content(request.content());
//序列化
std::string responseString;
response.SerializeToString(&responseString);
conn->send_message(responseString.c_str(), responseString.size(), msgid);
}
int main()
{
event_loop loop;
//加載配置文件
config_file::setPath("./serv.conf");
std::string ip = config_file::instance()->GetString("reactor", "ip", "0.0.0.0");
short port = config_file::instance()->GetNumber("reactor", "port", 8888);
printf("ip = %s, port = %d\n", ip.c_str(), port);
tcp_server server(&loop, ip.c_str(), port);
//注冊消息業務路由
server.add_msg_router(1, callback_busi);
loop.event_process();
return 0;
}
```
? 接下來是客戶端,客戶端我們創建一個Qps結構體,來記錄每秒,服務端成功回顯數據的次數,來做qps統計,客戶端我們可以指定開多少個線程去壓測服務端。
> example/qps_test/client.cpp
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <string>
#include "tcp_client.h"
#include "echoMessage.pb.h"
struct Qps
{
Qps() {
last_time = time(NULL);
succ_cnt = 0;
}
long last_time;//最后一次發包時間 ms為單位
int succ_cnt; //成功收到服務器回顯的次數
};
//客戶端業務
void busi(const char *data, uint32_t len, int msgid, net_connection *conn, void *user_data)
{
Qps *qps = (Qps*)user_data; //用戶參數
qps_test::EchoMessage request, response;
//解析服務端傳來的pb數據
if (response.ParseFromArray(data, len) == false) {
printf("server call back data error\n");
return;
}
//判斷數據內容是否回顯一致
if (response.content() == "Hello Lars!!!") {
//服務器請求響應成功一次
qps->succ_cnt ++;
}
long current_time = time(NULL);
if (current_time - qps->last_time >= 1) {
//如果當前時間比最后記錄時間大于1秒,那么我們進行記錄
printf("---> qps = %d <----\n", qps->succ_cnt);
qps->last_time = current_time;//記錄最后時間
qps->succ_cnt = 0;//清空成功次數
}
//給服務端發送新的請求
request.set_id(response.id() + 1);
request.set_content(response.content());
std::string requestString;
request.SerializeToString(&requestString);
conn->send_message(requestString.c_str(), requestString.size(), msgid);
}
//創建鏈接成功之后
void connection_start(net_connection *client, void *args)
{
qps_test::EchoMessage request;
request.set_id(1);
request.set_content("Hello Lars!!!");
std::string requestString;
request.SerializeToString(&requestString);
int msgid = 1;//與server端的消息路由一致
client->send_message(requestString.c_str(), requestString.size(), msgid);
}
void *thread_main(void *args)
{
//給服務端發包
event_loop loop;
tcp_client client(&loop, "127.0.0.1", 7777, "qps client");
Qps qps;
//設置回調
client.add_msg_router(1, busi, (void*)&qps);
//設置鏈接創建成功之后Hook
client.set_conn_start(connection_start);
loop.event_process();
return NULL;
}
int main(int argc, char **argv)
{
if (argc == 1) {
printf("Usage: ./client [threadNum]\n");
return 1;
}
//創建N個線程
int thread_num = atoi(argv[1]);
pthread_t *tids;
tids = new pthread_t[thread_num];
for (int i = 0; i < thread_num; i++) {
pthread_create(&tids[i], NULL, thread_main, NULL);
}
for (int i = 0; i < thread_num; i++) {
pthread_join(tids[i], NULL);
}
return 0;
}
```
接下來我們的Makefile
```makefile
CXX=g++
CFLAGS=-g -O2 -Wall -fPIC -Wno-deprecated
INC=-I../../include
LIB=-L../../lib -llreactor -lpthread -lprotobuf
OBJS = $(addsuffix .o, $(basename $(wildcard *.cc)))
all:
$(CXX) -o server $(CFLAGS) server.cpp echoMessage.pb.cc $(INC) $(LIB)
$(CXX) -o client $(CFLAGS) client.cpp echoMessage.pb.cc $(INC) $(LIB)
clean:
-rm -f *.o server client
```
? 記住編譯加上`-lprotobuf` 編譯的文件加上`echoMessage.pb.cc`文件。
### 14.2 并發測試結果
? 啟動服務端,再啟動客戶端,(注意問了方便看結果,將之前reactor的一些stdout的調試日志都關閉)
看客戶端運行結果
```bash
$ ./client 1
msg_router init...
do_connect EINPROGRESS
add msg cb msgid = 1
connect 127.0.0.1:7777 succ!
---> qps = 6875 <----
---> qps = 8890 <----
---> qps = 8354 <----
---> qps = 9078 <----
---> qps = 8933 <----
---> qps = 9043 <----
---> qps = 8743 <----
---> qps = 9048 <----
---> qps = 8987 <----
---> qps = 8742 <----
---> qps = 8720 <----
---> qps = 8795 <----
---> qps = 8889 <----
---> qps = 9034 <----
---> qps = 8669 <----
---> qps = 9001 <----
---> qps = 8810 <----
---> qps = 8850 <----
---> qps = 8802 <----
---> qps = 9072 <----
---> qps = 8853 <----
---> qps = 8804 <----
---> qps = 8574 <----
---> qps = 8645 <----
---> qps = 8085 <----
---> qps = 8720 <----
...
```
? 這里我們客戶端是開啟一個線程進行測試,平均每秒服務端會響應8700次左右。
? 這里我簡單用兩個主機,分別測試了一些數據
**主機1:**
> CPU個數:2個 , 內存: 2GB , 系統:Ubuntu18.04虛擬機
| 線程數 | QPS |
| ------ | ------- |
| 1 | 0.85w/s |
| 2 | 1.96w/s |
| 10 | 4.12w/s |
| 100 | 4.23w/s |
| 500 | 3.65w/s |
**主機2:**
> CPU個數: 24個 , 內存:128GB, 系統: 云主機
| 線程數 | QPS |
| ------ | -------- |
| 1 | 10.86w/s |
| 3 | 31.06w/s |
| 5 | 48.12w/s |
| 8 | 59.79w/s |
? 現在我們的Lars-Reactor模塊基本已經開完完成了,后續可能會再添加一些模塊,為了其他模塊方便使用Lars-Reactor的一些接口,我們可以對外提供一個公共的頭文件,方便一次性導入
> lars_reactor/include/lars_reactor.h
```c
#pragma once
#include "io_buf.h"
#include "buf_pool.h"
#include "reactor_buf.h"
#include "net_connection.h"
#include "tcp_conn.h"
#include "tcp_server.h"
#include "tcp_client.h"
#include "udp_server.h"
#include "udp_client.h"
#include "message.h"
#include "task_msg.h"
#include "event_loop.h"
#include "thread_pool.h"
#include "thread_queue.h"
#include "config_file.h"
```
---
### 關于作者:
作者:`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啟動工具腳本