目录
01Lars-lbAgentV0.2-赋值均衡数据结构关系分析
02 Lars-lbAgent0.2-host_info-load_balance-route_lb数据结构的定义
03Lars-lbAgentV0.2-proto协议的定义
04 Lars-lbAgentV0.2-route_lb与UDP server的关联
05 -Lars-lbAgentV0.2-route_lb与UDP server的关联
06Lars-lbAgentV0.2-route_lb更新host主机路由信
07Lars-lbAgentV0.2-route_lb获取host信息不存在
08 昨日回顾及agent获取host信息流程
09 Lars-LbAgentV0.2-Api获取主机信息-更新流程梳理
10 Lars-LbAgentV0.2-Api-load_balance模块更新主
11 Lars-LbAgentV0.2-Api目录结构创建
12 Lars-LbAgentV0.2-Api创建udp连接实现-lars-lbagent启动失败修正
13 Lars-LbAgentV0.2-Api-getHost请求实现
14 Lars-LbAgentV0.2-loadBalance-选择主机业务实现
15 Lars-LbAgentV0.3-api-get_hostAPI接口测试
16Lars-LbAgentV0.3-api-get_hostAPI流程总结
17 Lars-LbAgentV0.4-api-上报业务实现
01Lars-lbAgentV0.2-赋值均衡数据结构关系分析
# 七、Lars启动工具脚本
我们提供一个启动lars系统子系统和测试工具的一个脚本工具,`run_lars`
> Lars/run_lars
```shell
#!/bin/bash
LARS_REPORTER_PATH="./lars_reporter"
LARS_DNS_PATH="./lars_dns"
LARS_LBAGENT_PATH="./lars_loadbalance_agent"
LARS_WEB_PATH="./larsWeb"
LARS_API_EXAMPLE_PATH="./api/cpp/example"
usage()
{
echo ""
echo "=======启动子系统=========="
echo "Usage ./run_lars [reporter|dns|lbagent|web|test]"
echo
echo "=======测试工具============"
echo "Usage ./run_lars test gethost ModID CmdID"
echo "Usage ./run_lars test getroute ModID CmdID"
echo "Usage ./run_lars test report ModID CmdID IP Port 0|1 --- 0:SUCC, 1:OVERLOAD"
echo "Usage ./run_lars test simulator ModID CmdID [errRate(0-10)] [queryCnt(0-999999)]"
echo "Usage ./run_lars test qps ThreadNum"
echo "Usage ./run_lars test example ModID CmdID"
echo
}
if [ "$1" = "test" ]; then
if [ "$2" = "gethost" ]; then
$PWD/$LARS_API_EXAMPLE_PATH/get_host $3 $4
elif [ "$2" = "getroute" ]; then
$PWD/$LARS_API_EXAMPLE_PATH/get_route $3 $4
elif [ "$2" = "report" ]; then
$PWD/$LARS_API_EXAMPLE_PATH/report $3 $4 $5 $6 $7
elif [ "$2" = "example" ]; then
$PWD/$LARS_API_EXAMPLE_PATH/example $3 $4
elif [ "$2" = "simulator" ]; then
if [ $# -eq 4 ]; then
$PWD/$LARS_API_EXAMPLE_PATH/simulator $3 $4
elif [ $# -eq 5 ]; then
$PWD/$LARS_API_EXAMPLE_PATH/simulator $3 $4 $5
elif [ $# -eq 6 ]; then
$PWD/$LARS_API_EXAMPLE_PATH/simulator $3 $4 $5 $6
else
usage
fi
elif [ "$2" = "qps" ]; then
$PWD/$LARS_API_EXAMPLE_PATH/qps $3
fi
elif [ "$1" = "reporter" ]; then
cd $LARS_REPORTER_PATH
./bin/lars_reporter
elif [ "$1" = "dns" ]; then
cd $LARS_DNS_PATH
./bin/lars_dns
elif [ "$1" = "lbagent" ]; then
cd $LARS_LBAGENT_PATH
./bin/lars_lb_agent
elif [ "$1" = "web" ]; then
cd $LARS_WEB_PATH
./lars-web
elif [ "$1" = "help" ]; then
usage
else
usage
fi
```
02 Lars-lbAgent0.2-host_info-load_balance-route_lb数据结构的定义
**其他测试工具启动方式**
```bash
$ ./run_lars help
=======启动子系统==========
Usage ./run_lars [reporter|dns|lbagent|web|test]
=======测试工具============
Usage ./run_lars test gethost ModID CmdID
Usage ./run_lars test getroute ModID CmdID
Usage ./run_lars test report ModID CmdID IP Port 0|1 --- 0:SUCC, 1:OVERLOAD
Usage ./run_lars test simulator ModID CmdID [errRate(0-10)] [queryCnt(0-999999)]
Usage ./run_lars test qps ThreadNum
Usage ./run_lars test example ModID CmdID
```
03Lars-lbAgentV0.2-proto协议的定义
# 八、Lars优化建议
## 1) 量小的情况下过载发现太慢
对于一个小量服务, 假设一个窗口内(15s)匀速只有20次过程调用,如果远端过载, 则在已有策略上需要1succ 19err 几乎全失败才会感知
更严重的是,匀速<20次过程调用,即使全部失败也不可能达到过载条件
**优化:**
连续2个窗口内都没过载,但是这2个窗口内真实失败率都高于70%: 说明是小业务量,认为此节点过载
## 2) 某场景下节点过载发现太慢
04 Lars-lbAgentV0.2-route_lb与UDP server的关联
**场景回顾:**
远程服务mod的一台节点h已经挂了,但Lars系统一直没有将h节点判定为过载
**问题分析**
业务调用方的超时时间设置的是1秒,每次调用h节点都超时,即都消耗1秒,于是1秒才向Lb Agent上报一次状态
与此行为冲突的是,agent每隔15秒会重置所有正常节点的调用统计,15s内,agent仅收到15次对h节点调用错误的上报,即15个err,根本达不到h节点过载的条件,于是h节点在每个15s内都一直被Lars认为是正常的,永远无法被判定为过载
**结论**
05 -Lars-lbAgentV0.2-route_lb与UDP server的关联
**结论**
Lars的过载判定算法没有对节点调用耗时进行考虑
假设业务设置的调用超时时间为3秒,则业务方调用h节点失败消耗了3秒,并仅向Lars agent上报一个调用错误,`err + 1`
**优化:**
当调用节点失败,向上报状态要携带本次调用时间`timecost`(ms),Lb agent在收到调用失败的汇报时,会对失败个数根据`timecost`进行放大
具体是:
> ```
> errcnt = timecost / 100; errcnt = 1 if timecost = 0
> ```
于是乎,业务调用方调用h节点失败并消耗了1s,上报给Lb agent后,agent会认为h节点失败了1000ms/100 = 10次,会使挂掉的h更快达到过载条件
06Lars-lbAgentV0.2-route_lb更新host主机路由信
**本优化可以更快感知调用超时情况的过载**
## 3) 多线程分配如Dns、Report等 简单轮询改成一致性Hash算法
> MurmurHash一致性hash
### 3.1 什么是一致性HASH
> 注: 本小结内容转载自:<https://www.wmathor.com/index.php/archives/1138/>
工程设计中常用服务器集群来设计和实现数据缓存,以下是常见的策略:
1. 无论是添加、查询还是删除数据,都先将数据的 id 通过哈希函数转换成一个哈希值,记为 key
2. 如果目前机器有 N 台,则计算 key%N 值,这个值就是该数据所属的的机器编号,无论是添加、删除还是查询操作,都只在这台机器上进行
请分析这种缓存策略可能带来的问题,并提出改进的方案。
07Lars-lbAgentV0.2-route_lb获取host信息不存在
#### A. 普通 Hash 算法
缓存策略的潜在问题是如果增加或删除机器时(N 变化)代价会很高,所有的数据都不得不根据 id 重新计算一遍哈希值,并将哈希值对新的机器数进行取模操作,然后进行大规模的数据迁移。
#### B. 一致性Hash算法
为了解决这些问题,引入一致性哈希算法。假设数据的 id 通过哈希函数转换成的哈希值范围是 2的32次幂,也就是( 0, 2^32−1) 的数字空间中。我们将这些数字头尾相连,想象成一个闭合的环形,那么一个数字 id 在计算出哈希值之后认为对应到环中的一个位置上

08 昨日回顾及agent获取host信息流程
接下来,想象有三台机器也处于这样一个环中,这三台机器在环中的位置根据机器 id 计算出的哈希值来决定。那么一条数据如何确定归属哪台机器呢?首先把该数据的 id 用哈希值算出哈希值,并映射到环中的相应位置,然后顺时针找寻离这个位置最近的机器,那台机器就是该数据的归属。例如,下图有一个数据 m,计算其 hash 值后映射到环上,那么他的归属就是 2 号机器.

普通 hash 求余算法最为不妥的地方就是在有机器的添加或者删除之后会照成大量的对象存储位置失效,这样就大大的不满足单调性了。下面来分析一下一致性哈希算法是如何处理的.
09 Lars-LbAgentV0.2-Api获取主机信息-更新流程梳理
##### a. 结点(机器)删除
以上面的分布为例,如果 Node2(机器 2)出现故障被删除了,那么按照顺时针迁移的方法,Hash 值属于图中红色片段的所有数据将会被迁移到 Node3(机器)中,这样仅仅是红色的一段映射位置发生了变化,其它的对象没有任何的改动。如下图:

##### b. 结点(机器)添加
如果往集群中添加一个新的节点 NODE4,通过对应的哈希算法得到 KEY4,并映射到环中,如下图:

10 Lars-LbAgentV0.2-Api-load_balance模块更新主
按照顺时针迁移的规则,数据 Hash 值处于红色段的数据被迁移到了 Node4 中,其它对象还保持这原有的存储位置。通过对节点的添加和删除的分析,一致性哈希算法在保持了单调性的同时,数据的迁移时间达到了最小,这样的算法对分布式集群来说是非常合适的,避免了大量数据迁移,减小了服务器的的压力.
##### c. 一致性哈希算法优化
其实上面的一致性哈希函数还存在一个很大的问题,我们说 Hash 函数是输入的样本量很大的时候,其输出结果在输出域上是均匀分布的,但是这里假如只有三个输入,就很难保证分布是均匀的,有可能产生下图所示的分布,就导致负载极其不均衡

更加优化的一致性哈希算法引入了虚拟节点机制,即对每一台机器产生多个结点,称为虚拟节点。具体做法可以在机器 ip 或主机名的后面增加编号或端口号来实现。假设一台机器有 1000 个虚拟节点,3 台机器就有 3000 个结点,3000 个结点映射到哈希域上就相对比较均匀了.
11 Lars-LbAgentV0.2-Api目录结构创建
为了解决这种数据倾斜问题,一致性哈希算法引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点。具体做法可以在服务器ip或主机名的后面增加编号来实现。例如上面的情况,可以为每台服务器计算三个虚拟节点,于是可以分别计算 “Node A#1”、“Node A#2”、“Node A#3”、“Node B#1”、“Node B#2”、“Node B#3”的哈希值,于是形成六个虚拟节点:

> 更加详细的一致性hash介绍参考: <https://www.jianshu.com/p/92588bbe8a22>