## 8) 過期窗口清理與過載超時(V0.5)
? 本節為idle過期窗口清理與overload過載超時機制。
? 假設節點h長期處于idle,已經累計`vsucc = 100000, err = 0`;此時突然h過載,API調用產生了大量的錯誤,假設匯報了500個失敗,于是`verr = 500`,此時節點的失敗率0.5%,尚未被判定為過載;且因為之前長期運行的成功個數太多,使得需要更多的錯誤匯報才會讓LB感知到h過載
將idle節點每隔一定時間(默認15s)重置為idle初始狀態(`vsucc = 270, verr = 0`)
于是將節點的運行情況按照15s分隔,我們永遠只關注當前15s內此節點的狀態,于是過載的節點將會被更快感知.
? 首先,我們在配置文件中加入一些參數。
> lars_loadbalance_agent/conf/lars_lb_agent.conf
```ini
;整個窗口的真實失敗率閾值
window_err_rate=0.7
;對于某個modid/cmdid下的某個idle狀態的host,需要清理一次負載信息的周期
idle_timeout=15
;對于某個modid/cmdid/下的某個overload狀態的host,在過載隊列等待的最大時間
overload_timeout=15
```
? 我們在配置文件加載的時候,將這幾個參數一并加載。
> lars_loadbalance_agent/include/main_server.h
```c
#pragma once
#include "lars_reactor.h"
#include "lars.pb.h"
#include "route_lb.h"
struct load_balance_config
{
//經過若干次獲取請求host節點后,試探選擇一次overload過載節點
int probe_num;
//初始化host_info主機信息訪問成功的個數,防止剛啟動時少量失敗就認為過載
int init_succ_cnt;
//當idle節點切換至over_load時的初始化失敗次數,主要為了累計一定成功次數才能切換會idle
int init_err_cnt;
//當idle節點失敗率高于此值,節點變overload狀態
float err_rate;
//當overload節點成功率高于此值,節點變成idle狀態
float succ_rate;
//當idle節點連續失敗次數超過此值,節點變成overload狀態
int contin_err_limit;
//當overload節點連續成功次數超過此值, 節點變成idle狀態
int contin_succ_limit;
//當前agent本地ip地址(用于上報 填充caller字段)
uint32_t local_ip;
// *************************************
//整個窗口的真實失敗率閾值
float window_err_rate;
//對于某個modid/cmdid下的某個idle狀態的host,需要清理一次負載信息的周期
int idle_timeout;
//對于某個modid/cmdid/下的某個overload狀態的host,在過載隊列等待的最大時間
int overload_timeout;
};
// *************************************
```
> lars_loadbalance_agent/src/main_server.cpp
```c
static void init_lb_agent()
{
//1. 加載配置文件
config_file::setPath("./conf/lars_lb_agent.conf");
lb_config.probe_num = config_file::instance()->GetNumber("loadbalance", "probe_num", 10);
lb_config.init_succ_cnt = config_file::instance()->GetNumber("loadbalance", "init_succ_cnt", 180);
lb_config.init_err_cnt = config_file::instance()->GetNumber("loadbalance", "init_err_cnt", 5);
lb_config.err_rate = config_file::instance()->GetFloat("loadbalance", "err_rate", 0.1);
lb_config.succ_rate = config_file::instance()->GetFloat("loadbalance", "succ_rate", 0.92);
lb_config.contin_succ_limit = config_file::instance()->GetNumber("loadbalance", "contin_succ_limit", 10);
lb_config.contin_err_limit = config_file::instance()->GetNumber("loadbalance", "contin_err_limit", 10);
//*******************************************
lb_config.window_err_rate = config_file::instance()->GetFloat("loadbalance", "window_err_rate", 0.7);
lb_config.idle_timeout = config_file::instance()->GetNumber("loadbalance", "idle_timeout", 15);
lb_config.overload_timeout = config_file::instance()->GetNumber("loadbalance", "overload_timeout", 15);
//*******************************************
//2. 初始化3個route_lb模塊
create_route_lb();
//3. 加載本地ip
char my_host_name[1024];
if (gethostname(my_host_name, 1024) == 0) {
struct hostent *hd = gethostbyname(my_host_name);
if (hd)
{
struct sockaddr_in myaddr;
myaddr.sin_addr = *(struct in_addr*)hd->h_addr;
lb_config.local_ip = ntohl(myaddr.sin_addr.s_addr);
}
}
if (!lb_config.local_ip) {
struct in_addr inaddr;
inet_aton("127.0.0.1", &inaddr);
lb_config.local_ip = ntohl(inaddr.s_addr);
}
}
```
? 接下來我們要在load_balance進行`report()`的時候,之后可以觸發判定
> lars_loadbalance_agent/src/load_balance.cpp
```c
//上報當前host主機調用情況給遠端repoter service
void load_balance::report(int ip, int port, int retcode)
{
//定義當前時間
long current_time = time(NULL);
//...
//...
// 窗口檢查和超時機制
if (hi->overload == false) {
//節點是idle狀態
if (current_time - hi->idle_ts >= lb_config.idle_timeout) {
//時間窗口到達,需要對idle節點清理負載均衡數據
if (hi->check_window() == true) {
//將此節點 設置為過載
struct in_addr saddr;
saddr.s_addr = htonl(hi->ip);
printf("[%d, %d] host %s:%d change to overload cause windows err rate too high, read succ %u, real err %u\n",
_modid, _cmdid, inet_ntoa(saddr), hi->port, hi->rsucc, hi->rerr);
//設置為overload狀態
hi->set_overload();
//移出_idle_list,放入_overload_list
_idle_list.remove(hi);
_overload_list.push_back(hi);
}
else {
//重置窗口,回復負載默認信息
hi->set_idle();
}
}
}
else {
//節點為overload狀態
//那么處于overload的狀態時間是否已經超時
if (current_time - hi->overload_ts >= lb_config.overload_timeout) {
struct in_addr saddr;
saddr.s_addr = htonl(hi->ip);
printf("[%d, %d] host %s:%d reset to idle, vsucc %u, verr %u\n",
_modid, _cmdid, inet_ntoa(saddr), hi->port, hi->vsucc, hi->verr);
hi->set_idle();
//移出overload_list, 放入_idle_list
_overload_list.remove(hi);
_idle_list.push_back(hi);
}
}
}
```
? 每次report之前都會記錄一個當前時間,那么如果當前時間超過一定時間,`idle`和`overload`節點都會進行一次判定。
? **idle**
1. 如果當前時間超過idle的判定時間周期,需要堅持是否觸發窗口條件`check_windows()`
> lars_loadbalance_agent/src/host_info.cpp
```c
#include "host_info.h"
#include "main_server.h"
void host_info::set_idle()
{
vsucc = lb_config.init_succ_cnt;
verr = 0;
rsucc = 0;
rerr = 0;
contin_succ = 0;
contin_err = 0;
overload = false;
//*********************
idle_ts = time(NULL);//設置判定為idle狀態的時刻,也是重置窗口時間
//*********************
}
void host_info::set_overload()
{
vsucc = 0;
verr = lb_config.init_err_cnt;//overload的初試虛擬err錯誤次數
rsucc = 0;
rerr = 0;
contin_err = 0;
contin_succ = 0;
overload = true;
//*********************
overload_ts = time(NULL);//設置判定為overload的時刻
//*********************
}
//*********************
//計算整個窗口內的真實失敗率,如果達到連續失敗窗口值則返回true,代表需要調整狀態
bool host_info::check_window() {
if (rsucc + rerr == 0) {
return false; //分母不能為0
}
if (rerr * 1.0 / (rsucc + rerr) >= lb_config.window_err_rate) {
return true;
}
return false;
}
//*********************
```
2. 如果idle節點超過窗口的錯誤率判斷,那么需要更改idle狀態為overload
3. 將idle從`idle_list`摘掉,放在`overload_list`中。
**overload**
如果overload節點超過指定時間,那么需要將overload節點切換為idle,重新放回`idle_list`中。
---
### 關于作者:
作者:`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啟動工具腳本