LoadBalance负载均衡, 负责从多个 Invokers中选出具体的一个Invoker用于本次调用,调用过程中包含了负载均衡的算法,调用失败后需要重新选择
--->类注解@SPI说明可以基于Dubbo的扩展机制进行自定义的负责均衡算法实现,默认是随机算法方法注解@Adaptive说明能够生成设配方法Select方法设配类通过url的参数选择具体的算法, 在从invokers集合中根据具体的算法选择一个invoker
--->方法注解@Adaptive说明能够生成设配方法 Select方法设配类通过url的参数选择具体的算法, 在从invokers集合中根据具体的算法选择一个invoker
1. RandomLoadBalance: 随机访问策略,按权重设置随机概率,是默认策略
1)获取所有invokers的个数
2)遍历所有Invokers, 获取计算每个invokers的权重,并把权重累计加起来每相邻的两个invoker比较他们的权重是否一样,有一个不一样说明权重不均等
3)总权重大于零且权重不均等的情况下,按总权重获取随机数offset = random.netx(totalWeight);遍历invokers确定随机数offset落在哪个片段(invoker上)
4)权重相同或者总权重为0, 根据invokers个数均等选择invokers.get(random.nextInt(length))
2. RoundRobinLoadBalance:轮询,按公约后的权重设置轮询比率
1)获取轮询key 服务名+方法名
获取可供调用的invokers个数length
设置最大权重的默认值maxWeight=0
设置最小权重的默认值minWeight=Integer.MAX_VALUE
2)遍历所有Inokers,比较出得出maxWeight和minWeight
3)如果权重是不一样的
根据key获取自增序列
自增序列加一与最大权重取模默认得到currentWeigth
遍历所有invokers筛选出大于currentWeight的invokers
设置可供调用的invokers的个数length
4)自增序列加一并与length取模,从invokers获取invoker
3. LeastActiveLoadBalance: 最少活跃调用数, 相同的活跃的随机选择,
活跃数是指调用前后的计数差, 使慢的提供者收到更少的请求,因为越慢的提供者前后的计数差越大。
活跃计数的功能消费者是在ActiveLimitFilter中设置的
4. 最少活跃的选择过程如下:
1)获取可调用invoker的总个数
初始化最小活跃数,相同最小活跃的个数
相同最小活跃数的下标数组
等等
2)遍历所有invokers, 获取每个invoker的获取数active和权重
找出最小权重的invoker
如果有相同最小权重的inovkers, 将下标记录到数组leastIndexs[]数组中
累计所有的权重到totalWeight变量
3)如果invokers的权重不相等且totalWeight大于0
按总权重随机offsetWeight = random.nextInt(totalWeight)
计算随机值在哪个片段上并返回invoker
4)如果invokers的权重相等或者totalWeight等于0,均等随机
5. ConsistentHashLoadBalance:一致性hash, 相同参数的请求总是发到同一个提供者,当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。对于一致性哈希算法介绍网上很多
==============================个人学习============================
【一】Dubbo的四种负载均衡算法学习
package com.sxf.test.dubbo; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Dubbo默认实现的四种负载均衡的算法解析
* @author sxf
*
*/
public class TestDubboLoadBalance {
private static Random random = new Random(); public static void main(String[] args) {
//随机负载的策略
//testRandomLoadBalance(); //轮询的策略
testRoundRobinLoadBalance();
} /**
* Dubbo基于随机和权重算法实现的随机负载均衡的核心算法
*/
public static void testRandomLoadBalance(){
List<Invoker> invokers=new ArrayList<Invoker>();
//a机器
Invoker a=new Invoker();
a.setName("a");
a.setWeight(100); //b机器
Invoker b=new Invoker();
b.setName("b");
b.setWeight(300); //c机器
Invoker c=new Invoker();
c.setName("c");
c.setWeight(800); //添加机器列表
invokers.add(a);
invokers.add(b);
invokers.add(c);
int length=3;//三台机器 //权重不同的算法
if(false){
int totalWeight=1200;//三台机器总权重
int offset=random.nextInt(totalWeight);//随机一个小于总权重的数字
System.out.println("基于本次总权重随机出来的数字:"+offset);
for(int i=0;i<length;i++){
offset-=invokers.get(i).getWeight();
if(offset<0){
System.out.println("本次被负载到的机器为:"+invokers.get(i).getName() +" ,权重为:"+invokers.get(i).getWeight() );
return;
}
}
} //权重相同则按随机数,随机到那台机器,就是那台机器
int index=random.nextInt(length);
System.out.println("机器列表中随机一个数字为:"+index);
System.out.println("本次被负载到的机器为:"+invokers.get(index).getName());
} /**
* Dubbo的轮询调用策略算法
*
* dubbo的轮询策略,是基于【本次调用列表的list集合的下标=调用次数%服务列表个数】实现每个服务都循环调用。
* 唯独不同的是,对权重大的服务列表多轮询一次。是通过对最大权重进行取余数操作一次,刨除本轮轮询小权重的服务列表。
*
*
*/
public static void testRoundRobinLoadBalance(){
//key代表集群中的一个服务提供者的一个接口,value表示走当前消费服务器调用服务提供者的调用次数
Map<String,AtomicInteger> sequences=new ConcurrentHashMap<String,AtomicInteger>();
sequences.put("provider", new AtomicInteger()); //服务集群列表
List<Invoker> invokers=new ArrayList<Invoker>(); int length=4;
//a机器
Invoker a=new Invoker();
a.setName("a");
a.setWeight(100); //b机器
Invoker b=new Invoker();
b.setName("b");
b.setWeight(300); //c机器
Invoker c=new Invoker();
c.setName("c");
c.setWeight(800); //d机器
Invoker d=new Invoker();
d.setName("d");
d.setWeight(800); //添加机器列表
invokers.add(a);
invokers.add(b);
invokers.add(c);
invokers.add(d); //调用次数统计
Map<String,Integer> map=new HashMap<String, Integer>(); //模拟100次调用,服务提供者总共有4台机器
for(int i=0;i<100;i++){
String key="provider";//服务提供者的key
AtomicInteger integer=sequences.get(key);
int count=integer.get();
integer.compareAndSet(count,count+1);
//获取本次调用的服务
Invoker invoker=invokers.get(count%length); //统计本次调用服务的次数
Integer m=map.get(invoker.getName());
if(m==null){
map.put(invoker.getName(), new Integer(1));
}else{
int newc=m.intValue()+1;
map.put(invoker.getName(), new Integer(newc));
}
} //打印每次服务的调用次数
for(String key:map.keySet()){
System.out.println("服务【"+key+"】的调用次数"+map.get(key));
}
} /**
* 最小活跃次数,每一个服务在调用的时候,都会通过Filter
* com.alibaba.dubbo.rpc.filter.ActiveLimitFilter
* com.alibaba.dubbo.rpc.filter.ActiveLimitFilter.ExecuteLimitFilter
* 向全局的com.alibaba.dubbo.rpc.RpcStatus中统计当前调用服务提供者的活跃次数
*
* 每次在负载均衡的时候,都会通过当前服务列表去RpcStatus中拿到自己的活跃数。
* 形成一个最小活跃数的数组 int[] lestActive中记录最小活跃数或相同最小活跃数的在当前服务列表的下标。
* 如果最后最小活跃数只有一个服务提供者,则本次负载均衡就走这台服务器
* 如果最后最小活跃数相同的,就在数组中随机一个下标,从服务列表的List集合中取出一个,负载均衡到这台服务器
*
*/
public static void testLeastActiveLoadBalance(){
//下面是伪代码
List<Invoker> invokers=new ArrayList<Invoker>();
int length=invokers.size();
int[] leastIndexs = new int[length];
int lestActive=-1;//最小活跃数
int lestActiveCount=0;
for(int i=0;i<invokers.size();i++){
Invoker invoker=invokers.get(i);
Integer active=1;//从RpcStatus中拿到当前invoker的活跃次数
if(lestActive==-1||active<lestActive){
lestActive=active;
leastIndexs[0]=i;
}else if(active==lestActive){
leastIndexs[lestActiveCount++]=i;
}
}
//从相同的最小活跃数中随机选举出一个负载到这台机器上
Invoker invoker=invokers.get(leastIndexs[random.nextInt(lestActiveCount)]);
} /**
* 这个是通过你对服务提供者接口配置调用服务的参数列表,进行hash运算,然后在服务列表中基于一致性hash算法,(hash环),算出负载到那台机器节点。
* 结果,就是相同参数的调用,最终会一直被负载均衡到同一个机器节点上。前提是机器节点的个数不变。
*
* 配置如: <dubbo:service interface="..." loadbalance="consistenthash" />
或: <dubbo:reference interface="..." loadbalance="consistenthash" />
或: <dubbo:service interface="...">
<dubbo:method name="..." loadbalance="consistenthash"/>
</dubbo:service>
或: <dubbo:reference interface="...">
<dubbo:method name="..." loadbalance="consistenthash"/>
</dubbo:reference> 缺省只对第一个参数Hash,如果要修改,请配置
<dubbo:parameter key="hash.arguments" value="0,1" /> 缺省用160份虚拟节点,如果要修改,请配置
<dubbo:parameter key="hash.nodes" value="320" />
*/
public static void testConsistentHashLoadBalance(){ }
} /**
* 代表服务的机器节点
* @author sxf
*
*/
class Invoker{
//机器节点
private String name;
//权重
private int weight;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
} }