30.1 基础
每个模块`modid/cmdid`下有若干节点,节点的集合称为此模块的路由; 对于每个节点,有两种状态:
idle:此节点可用,可作为API(相当于Agent的客户端)请求的节点使用;overload:此节点过载,暂时不可作为API请求的节点使用
在请求节点时,有几个关键属性:- 虚拟成功次数
vsucc,API汇报节点调用结果是成功时,该值+1 - 虚拟失败次数
verr,API汇报节点调用结果是失败时,该值+1 - 连续成功次数
contin_succ,连续请求成功的次数 - 连续失败次数
contin_err,连续请求失败的次数
这4个字段,在节点状态改变时(idle<—>overload),会被重置。
30.2 调度方式
- 图1

- 图2

如图所示,整体的调度节点的方式大致如下。这里每个节点就是一个Host主机信息,也就是我们需要被管理的主机信息,一个主机信息应该包括基本的ip和port还有一些其他属性。
如图1,API相当于我们的Agent模块的客户端,也是业务端调用的请求主机接口。 API发送GetHost请求,发送给Agent的server端,传递信息包括modID/cmdID. AgentServer 使用UDPserver处理的API网络请求,并且交给了某个”负载均衡算法”.
如图2,一个负载均衡算法,我们称之为是一个”load balance”, 一个”load balance”对应针对一组modID/cmdID下挂在的全部host信息进行负载。每个”load balance”都会有两个节点队列。一个队列是”idle_list”,存放目前可用的Host主机信息(ip+port), 一个队列是”overload_list”,存放目前已经过载的Host主机信息(ip+port).
当API对某模块发起节点获取时:
- Load Balance从空闲队列拿出队列头部节点,作为选取的节点返回,同时将此节点重追到队列尾部;
- probe机制 :如果此模块过载队列非空,则每经过
probe_num次节点获取后(默认=10),给过载队列中的节点一个机会,从过载队列拿出队列头部节点,作为选取的节点返回,让API试探性的用一下,同时将此节点重追到队列尾部; - 如果空闲队列为空,说明整个模块过载了,返回过载错误;且也会经过
probe_num次节点获取后(默认=10),给过载队列中的节点一个机会,从过载队列拿出队列头部节点,作为选取的节点返回,让API试探性的用一下,同时将此节点重追到队列尾部;
调度就是:从空闲队列轮流选择节点;同时利用probe机制,给过载队列中节点一些被选择的机会
30.2 API层与Agent Load Balance的通信协议
/Lars/base/proto/lars.proto
syntax = "proto3";package lars;/* Lars系统的消息ID */enum MessageId {ID_UNKNOW = 0; //proto3 enum第一个属性必须是0,用来占位ID_GetRouteRequest = 1; //向DNS请求Route对应的关系的消息IDID_GetRouteResponse = 2; //DNS回复的Route信息的消息IDID_ReportStatusRequest = 3; //上报host调用状态信息请求消息IDID_GetHostRequest = 4; //API 发送请求host信息给 Lb Agent模块 消息IDID_GetHostResponse = 5; //agent 回执给 API host信息的 消息ID}enum LarsRetCode {RET_SUCC = 0;RET_OVERLOAD = 1; //超载RET_SYSTEM_ERROR = 2; //系统错误RET_NOEXIST = 3; //资源不存在}//...//...// API 请求agent 获取host信息 (UDP)message GetHostRequest {uint32 seq = 1;int32 modid = 2;int32 cmdid = 3;}// Agent回执API的 host信息 (UDP)message GetHostResponse {uint32 seq = 1;int32 modid = 2;int32 cmdid = 3;int32 retcode = 4;HostInfo host = 5;}
这里主要增加两个ID:ID_GetHostRequest和ID_GetHostResponse,即,API的getHost请求的发送ID和回收ID。其中两个ID对应的message包为GetHostRequest和GetHostResponse。
30.3 host_info与Load Balance初始化
我们首先应该定义几个数据结构,分别是
host_info:表示一个host主机的信息
load_balance:针对一组modid/cmdid的负载均衡模块
route_lb:一共3个,和agent的udp server(提供api服务)的数量一致,一个server对应一个route_lb,每个route_lb负责管理多个load_balance

host_info
lars_loadbalance_agent/include/host_info.h
#pragma once/** 被代理的主机基本信息** */struct host_info {host_info(uint32_t ip, int port, uint32_t init_vsucc):ip(ip),port(port),vsucc(init_vsucc),verr(0),rsucc(0),rerr(0),contin_succ(0),contin_err(0),overload(false){//host_info初始化构造函数}uint32_t ip; //host被代理主机IPint port; //host被代理主机端口uint32_t vsucc; //虚拟成功次数(API反馈),用于过载(overload),空闲(idle)判定uint32_t verr; //虚拟失败个数(API反馈),用于过载(overload),空闲(idle)判定uint32_t rsucc; //真实成功个数, 给Reporter上报用户观察uint32_t rerr; //真实失败个数,给Reporter上报用户观察uint32_t contin_succ; //连续成功次数uint32_t contin_err; //连续失败次数bool overload; //是否过载};
host_info包含,最关键的主机信息ip+port.除了这个还有一些用户负载均衡算法判断的属性。
load_balance
lars_loadbalance_agent/include/load_balance.h
#pragma once#include <ext/hash_map>#include <list>#include "host_info.h"#include "lars.pb.h"//ip + port为主键的 host信息集合typedef __gnu_cxx::hash_map<uint64_t, host_info*> host_map; // key:uint64(ip+port), value:host_infotypedef __gnu_cxx::hash_map<uint64_t, host_info*>::iterator host_map_it;//host_info list集合typedef std::list<host_info*> host_list;typedef std::list<host_info*>::iterator host_list_it;/** 负载均衡算法核心模块* 针对一组(modid/cmdid)下的全部host节点的负载规则*/class load_balance {public:load_balance(int modid, int cmdid):_modid(modid),_cmdid(cmdid){//load_balance 初始化构造}//判断是否已经没有host在当前LB节点中bool empty() const;//从当前的双队列中获取host信息int choice_one_host(lars::GetHostResponse &rsp);//如果list中没有host信息,需要从远程的DNS Service发送GetRouteHost请求申请int pull();//根据dns service远程返回的结果,更新_host_mapvoid update(lars::GetRouteResponse &rsp);//当前load_balance模块的状态enum STATUS{PULLING, //正在从远程dns service通过网络拉取NEW //正在创建新的load_balance模块};STATUS status; //当前的状态private:int _modid;int _cmdid;int _access_cnt; //请求次数,每次请求+1,判断是否超过probe_num阈值host_map _host_map; //当前load_balance模块所管理的全部ip + port为主键的 host信息集合host_list _idle_list; //空闲队列host_list _overload_list; //过载队列};
一个load_balance拥有两个host链表集合,还有一个以ip/port为主键的_host_map集合。
属性:
_idle_list:全部的空闲节点Host集合列表。
_overload_list:全部的过载节点Host集合列表。
_host_map:当前load_balance中全部所管理的ip+port是host总量,是一个hash_map<uint64_t, host_info*>类型。
_modid,_cmdid:当前load_balance所绑定的modid/cmdid。
_access_cnt:记录当前load_balance被api的请求次数,主要是用户判断是否触发probe机制(从overload_list中尝试取节点)
status: 当前load_balance所处的状态。包括PULLING,NEW. 是当load_balance在向远程dns service请求host 的时候,如果正在下载中,则为PULLING状态,如果是增加被创建则为NEW状态。
方法
choice_one_host():根据负载均衡算法从两个list中取得一个可用的host信息放在GetRouteResponse &rsp中
pull():如果list中没有host信息,需要从远程的DNS Service发送GetRouteHost请求申请。
update():根据dns service远程返回的结果,更新_host_map
以上方法我们暂时先声明,暂不实现,接下来,我们来定义route_lb数据结构
route_lb
/lars_loadbalance_agent/include/route_lb.h
#pragma once#include "load_balance.h"//key: modid+cmdid value: load_balancetypedef __gnu_cxx::hash_map<uint64_t, load_balance*> route_map;typedef __gnu_cxx::hash_map<uint64_t, load_balance*>::iterator route_map_it;/** 针对多组modid/cmdid ,route_lb是管理多个load_balanace模块的* 目前设计有3个,和udp-server的数量一致,每个route_lb分别根据* modid/cmdid的值做hash,分管不同的modid/cmdid** */class route_lb {public://构造初始化route_lb(int id);//agent获取一个host主机,将返回的主机结果存放在rsp中int get_host(int modid, int cmdid, lars::GetHostResponse &rsp);//根据Dns Service返回的结果更新自己的route_lb_mapint update_host(int modid, int cmdid, lars::GetRouteResponse &rsp);private:route_map _route_lb_map; //当前route_lb下的管理的loadbalancepthread_mutex_t _mutex;int _id; //当前route_lb的ID编号};
属性
_route_lb_map: 当前route_lb下的所管理的全部load_balance(一个load_balance负责一组modid/cmdid的集群负载)。其中key是modid/cmdid,value则是load_balance对象.
_mutex:保护_route_lb_map的锁。
_id:当前route_lb的id编号,编号从1-3,与udpserver的数量是一致的。一个agent udp server对应一个id。
方法
get_host():直接处理API业务层发送过来的ID_GetHostRequest请求。agent获取一个host主机,将返回的主机结果存放在rsp中.
update_host():这个是load_balance触发pull()操作,远程Dns Service会返回结果,route_lb根据Dns Service返回的结果更新自己的_route_lb_map.
