【DUBBO】dubbo的LoadBalance接口
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; } }