一种服务器的负载均衡选取算法

来源:互联网 发布:风油精滴b 知乎 编辑:程序博客网 时间:2024/06/07 06:41

在很多分布式系统里面会遇到一个均衡节点选取的问题:一般是1个负载管理服务器,多个应用服务单元。当有连接或者业务来是,先会去询问负载管理器获取一个负载轻的服务单元,一般的选取就是选取负载最轻的那个。通常情况下是不会有问题的,如果你的应用服务器单元跑的是类似视频服务这种应用,就会出现这样一种情况,某个视频服务A崩溃或者异常了,这个视频服务的所有用户在瞬间会转移到负载最轻的B上,这个时候可能B也异常了,B 和A的用户又全部转到C,这样会产生多咪若效应,所有的服务在瞬间全部不可用。碰到这样的问题我们该怎么避免?问题的根本是负载评估是周期性的,不是每接入一个用户就报一次负载给负载管理器。最理想的是,根据负载管理上所有单元的负载做权重随机。

算法描述如下

假设有N个服务单元,(服务单元的负载是1 ~ 100,100是满负荷运转)。他们的负载分别是L1 L2 L3 .... Ln。

第i台服务的选中的权值 Ti = 100 - Li;

那么整个掉落区间就为 Mn= T1 + T2 + T3  + ... + Tn;

T0 = 0;

随进产生一个0 ~ Mn之间的数V,如果 V < T0 + T1 +...Ti 并且V > T0 + T1 + ... +T(i - 1),那么就 选取第i台服务作为处理新业务的服务单元。


代码实现:

节点负载结构:

typedef struct NodeLoadInfo{uint32_tnode_id;//节点IDuint16_tnode_load;//节点负载值,0到100,100表示负载最大NodeLoadInfo(){node_id = 0;node_load = 100;};}NodeLoadInfo;
选取区间结构:

typedef struct NodeRange{int32_tmin_value;//T0 + T1 + ... + T(i-1)int32_tmax_value;//T0 + T1 + ... + Tiuint32_tnode_id;//节点的服务IDNodeRange(){min_value = 0;max_value = 0;node_id = 0;};}NodeRange;
负载选取器的实现接口:

class CNodeLoadManager{public:CNodeLoadManager();~CNodeLoadManager();voidadd_node(const NodeLoadInfo& info);//添加一个新的节点负载voidupdate_node(const NodeLoadInfo& info);//更新一个节点的负载voiddel_node(uint32_t node_id);//删除一个节点uint32_tselect_node();//选取一个适合的节点private:NodeLoadInfoMapnode_info_map_;//节点负载表NodeRangeArraynode_ranges_;//一定周期的概率区间表boolcreate_range_;//是否需要重建概率选取表int32_tregion_;//概率全区间};
在上面的接口当中,select_node是核心函数,一下是select_node的伪代码实现:

uint32_t select_node(){uint32_t ret = 0;node_ranges_.clear();region_ = 0;int32_t interval = 0;NodeRange range;//计算各个节点的掉落区间for(NodeLoadInfoMap::iterator it = node_info_map_.begin(); it != node_info_map_.end(); ++it){if(it->second.node_load <= MAX_LOAD_VALUE) //计算负载区间,如果负载太大,不参与计算{interval = MAX_LOAD_VALUE - it->second.node_load + 1;range.node_id = it->second.node_id;range.min_value = region_;region_ += interval;range.max_value = region_ - 1;node_ranges_.push_back(range);}}if(region_ > 0) //随机概率选取ret = locate_server(region_);return ret;}
在上面的代码中,实现了区间的计算。locate_server函数查找对应的服务节点,伪代码:

uint32_t locate_server(int32_t region){uint32_t ret = 0;//产生一个随机数int32_t seed = rand() % region;int32_t ranges_size = node_ranges_.size();int32_t max_pos = ranges_size;int32_t min_pos = 0;int32_t pos = 0;//2分法查询掉落到的节点do {pos = (min_pos + max_pos) / 2;if(pos > ranges_size - 1){ret = node_ranges_[ranges_size - 1].node_id;break;}if(pos < 0){ret = node_ranges_[0].node_id;break;}if(node_ranges_[pos].max_value >= seed && node_ranges_[pos].min_value <= seed){ret = node_ranges_[pos].node_id;break;}else if(node_ranges_[pos].max_value < seed){min_pos = pos + 1;}else if(node_ranges_[pos].min_value > seed){max_pos = pos - 1;}} while (true);return ret;}

完全的代码在revolver的base_nodes_load.h base_nodes_load.cpp之中。


测试结果:

我们模拟了10个节点,随机指定10 ~ 100的负载值,然后选取1000次业务处理的节点,结果如下图:



从结果看来是比较均衡的选取,比较理想。



0 0
原创粉丝点击