当前位置:   article > 正文

Dubbo负载均衡策略_dubbo负载均衡配置

dubbo负载均衡配置

目录

RandomLoadBalance

RoundRobinLoadBalance

LeastActiveLoadBalance

ConsistentHashLoadBalance


RandomLoadBalance

RandomLoadBalance即随机调用实现负载均衡是Dubbo默认的一种策略,加权随机算法对provider不同实例设置不同的权重,按照权重来负载均衡,权重越大分配流量越高,一般用这个默认的就可以了。

算法思想

假设有一组服务器servers = [A, B, C],他们对应的权重为weights = [5, 3, 2],权重总和为 10。现在把这些权重值平铺在一维坐标值上,[0, 5)区间属于服务器A,[5, 8)区间属于服务器 B,[8, 10)区间属于服务器C。接下来通过随机数生成器生成一个范围在[0, 10)之间的随机数,然后计算这个随机数会落到哪个区间上。比如数字3会落到服务器A对应的区间上,此时返回服务器A即可。权重越大的机器,在坐标轴上对应的区间范围就越大,因此随机数生成器生成的数字就会有更大的概率落到此区间内。只要随机数生成器产生的随机数分布性很好,在经过多次选择后,每个服务器被选中的次数比例接近其权重比例。比如经过一万次选择后,服务器A被选中的次数大约为5000次,服务器B被选中的次数约为3000次,服务器C被选中的次数约为2000次。

RoundRobinLoadBalance

8核 + 16G机器申请了2台,4核 + 8G的机器一台,两台8核16G的机器设置权重4,4核8G的机器设置权重2,默认是将流量均匀地打到各个机器上去,如果每个机器的性能不一样,容易导致性能差的机器负载过高。所以需要调整权重让性能差的机器承载权重小一些流量少一些,即加权轮询算法。

算法思想

使用本地权重表,根据调用情况动态调整。每次调用根据算法更新权重表,设置本地权重为本地所有权重加上配置权重,选出本地权重最大的服务,并设置它的本地权重减去本轮总权重。权重表回收,删除1分内未被调用的实例。预热期权重算法,预热期默认10分钟,warmWeight = uptime/(warmup/weight),如20权重服务,在启动5分钟时的预热权重 = 5/(10/20) = 5/0.5=10。

原始权重:服务设置中的weight

动态权重:每次选取操作调整后的权重

动态权重总和:每次调整完后的所有服务动态权重总和

本地动态权重表:记录本地服务选取时的动态权重信息,每次调用选取算法都会更新

  1. package org.apache.dubbo.rpc.cluster.loadbalance;
  2. import org.apache.dubbo.common.URL;
  3. import org.apache.dubbo.rpc.Invocation;
  4. import org.apache.dubbo.rpc.Invoker;
  5. import java.util.Collection;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.concurrent.ConcurrentHashMap;
  9. import java.util.concurrent.ConcurrentMap;
  10. import java.util.concurrent.atomic.AtomicBoolean;
  11. import java.util.concurrent.atomic.AtomicLong;
  12. /**
  13. * 轮询负载均衡策略
  14. * Round robin load balance.
  15. */
  16. public class RoundRobinLoadBalance extends AbstractLoadBalance {
  17. //策略名称
  18. public static final String NAME = "roundrobin";
  19. //动态权重更新时间
  20. private static final int RECYCLE_PERIOD = 60000;
  21. //路由权重
  22. protected static class WeightedRoundRobin {
  23. private int weight;
  24. //动态权重
  25. private AtomicLong current = new AtomicLong(0);
  26. //最后选取时间
  27. private long lastUpdate;
  28. public int getWeight() {
  29. return weight;
  30. }
  31. public void setWeight(int weight) {
  32. this.weight = weight;
  33. current.set(0);
  34. }
  35. //每次选取操作增加原始权重
  36. public long increaseCurrent() {
  37. return current.addAndGet(weight);
  38. }
  39. //每次选中减去动态总权重
  40. public void sel(int total) {
  41. current.addAndGet(-1 * total);
  42. }
  43. public long getLastUpdate() {
  44. return lastUpdate;
  45. }
  46. public void setLastUpdate(long lastUpdate) {
  47. this.lastUpdate = lastUpdate;
  48. }
  49. }
  50. private ConcurrentMap<String, ConcurrentMap<String, WeightedRoundRobin>> methodWeightMap = new ConcurrentHashMap<String, ConcurrentMap<String, WeightedRoundRobin>>();
  51. //更新锁
  52. private AtomicBoolean updateLock = new AtomicBoolean();
  53. /**
  54. * get invoker addr list cached for specified invocation
  55. * <p>
  56. * <b>for unit test only</b>
  57. * 获取url对应的权重路由
  58. * 结构如下:
  59. * {
  60. * "bike.get":{
  61. * "url1": WeightedRoundRobin,
  62. * "url2": WeightedRoundRobin,
  63. * ...
  64. * },
  65. * "bike.update:{
  66. * "url1": WeightedRoundRobin,
  67. * "url2": WeightedRoundRobin,
  68. * ...
  69. * }
  70. * }
  71. * @param invokers
  72. * @param invocation
  73. * @return
  74. */
  75. protected <T> Collection<String> getInvokerAddrList(List<Invoker<T>> invokers, Invocation invocation) {
  76. String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
  77. //获取url对应的权重路由
  78. Map<String, WeightedRoundRobin> map = methodWeightMap.get(key);
  79. if (map != null) {
  80. return map.keySet();
  81. }
  82. return null;
  83. }
  84. /**
  85. * 根据动态权重表选取服务
  86. * @param invokers 实例列表
  87. * @param url 请求url 在这没啥用
  88. * @param invocation 请求调用信息
  89. * @param <T>
  90. * @return 选出的实例调度器
  91. */
  92. @Override
  93. protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
  94. String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
  95. //获取url对应的动态权重表
  96. ConcurrentMap<String, WeightedRoundRobin> map = methodWeightMap.get(key);
  97. //如果权重表为空,则新建
  98. if (map == null) {
  99. methodWeightMap.putIfAbsent(key, new ConcurrentHashMap<String, WeightedRoundRobin>());
  100. map = methodWeightMap.get(key);
  101. }
  102. //动态权重总和,用于计算更新动态权重
  103. int totalWeight = 0;
  104. //计算时动态权重最小值
  105. long maxCurrent = Long.MIN_VALUE;
  106. //当前时间,设置为动态权重表最后选取时间
  107. long now = System.currentTimeMillis();
  108. Invoker<T> selectedInvoker = null;
  109. WeightedRoundRobin selectedWRR = null;
  110. //循环所有注册服务
  111. for (Invoker<T> invoker : invokers) {
  112. //获取服务id
  113. String identifyString = invoker.getUrl().toIdentityString();
  114. //获取服务对应的本地动态权信息
  115. WeightedRoundRobin weightedRoundRobin = map.get(identifyString);
  116. //获取权重,预热期返回预热权重,否则为原始权重
  117. int weight = getWeight(invoker, invocation);
  118. //新建本地动态权重信息
  119. if (weightedRoundRobin == null) {
  120. weightedRoundRobin = new WeightedRoundRobin();
  121. weightedRoundRobin.setWeight(weight);
  122. map.putIfAbsent(identifyString, weightedRoundRobin);
  123. }
  124. //是否为预热权重,预热情况更新权重
  125. if (weight != weightedRoundRobin.getWeight()) {
  126. //weight changed
  127. weightedRoundRobin.setWeight(weight);
  128. }
  129. //每次选取调整对应的动态选择
  130. long cur = weightedRoundRobin.increaseCurrent();
  131. //更新最后选取时间,为什么不在increaseCurrent方法里面更新?
  132. //入long cur = weightedRoundRobin.increaseCurrent(now);
  133. weightedRoundRobin.setLastUpdate(now);
  134. //获取最大权重服务
  135. if (cur > maxCurrent) {
  136. maxCurrent = cur;
  137. selectedInvoker = invoker;
  138. selectedWRR = weightedRoundRobin;
  139. }
  140. //相加计算总的权重
  141. totalWeight += weight;
  142. }
  143. //移除过期的实例,默认60秒没访问移除
  144. //调度器数和权重集合数不一致是,更新权重集合
  145. if (!updateLock.get() && invokers.size() != map.size()) {
  146. if (updateLock.compareAndSet(false, true)) {
  147. try {
  148. // copy -> modify -> update reference
  149. ConcurrentMap<String, WeightedRoundRobin> newMap = new ConcurrentHashMap<>(map);
  150. newMap.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > RECYCLE_PERIOD);
  151. methodWeightMap.put(key, newMap);
  152. } finally {
  153. updateLock.set(false);
  154. }
  155. }
  156. }
  157. //减少选中服务的动态权重值
  158. if (selectedInvoker != null) {
  159. selectedWRR.sel(totalWeight);
  160. return selectedInvoker;
  161. }
  162. // should not happen here
  163. // 没有选出调度器的时候返回第一个服务。
  164. return invokers.get(0);
  165. }
  166. }
  1. package org.apache.dubbo.rpc.cluster.loadbalance;
  2. import org.apache.dubbo.common.URL;
  3. import org.apache.dubbo.common.utils.CollectionUtils;
  4. import org.apache.dubbo.rpc.Invocation;
  5. import org.apache.dubbo.rpc.Invoker;
  6. import org.apache.dubbo.rpc.cluster.LoadBalance;
  7. import java.util.List;
  8. import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY;
  9. import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_WARMUP;
  10. import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_WEIGHT;
  11. import static org.apache.dubbo.rpc.cluster.Constants.WARMUP_KEY;
  12. import static org.apache.dubbo.rpc.cluster.Constants.WEIGHT_KEY;
  13. /**
  14. * AbstractLoadBalance
  15. */
  16. public abstract class AbstractLoadBalance implements LoadBalance {
  17. /**
  18. * Calculate the weight according to the uptime proportion of warmup time
  19. * the new weight will be within 1(inclusive) to weight(inclusive)
  20. * 计算预热期权重,最小为1
  21. * warmWeight = uptime/(warmup/weight),
  22. * 如20权重服务,在启动5分钟时的预热权重 = 5/(10/20) = 5/0.5=10
  23. * @param uptime the uptime in milliseconds 上线时间
  24. * @param warmup the warmup time in milliseconds 预热时间
  25. * @param weight the weight of an invoker 原值权重
  26. * @return weight which takes warmup into account
  27. */
  28. static int calculateWarmupWeight(int uptime, int warmup, int weight) {
  29. int ww = (int) ( uptime / ((float) warmup / weight));
  30. return ww < 1 ? 1 : (Math.min(ww, weight));
  31. }
  32. @Override
  33. public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
  34. if (CollectionUtils.isEmpty(invokers)) {
  35. return null;
  36. }
  37. if (invokers.size() == 1) {
  38. return invokers.get(0);
  39. }
  40. return doSelect(invokers, url, invocation);
  41. }
  42. protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation);
  43. /**
  44. * Get the weight of the invoker's invocation which takes warmup time into account
  45. * if the uptime is within the warmup time, the weight will be reduce proportionally
  46. * 获取调用程序的调用权重,其中考虑了预热时间如果正常运行时间在预热时间内,则权重将按比例减少
  47. * @param invoker the invoker
  48. * @param invocation the invocation of this invoker
  49. * @return weight
  50. */
  51. int getWeight(Invoker<?> invoker, Invocation invocation) {
  52. int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(), WEIGHT_KEY, DEFAULT_WEIGHT);
  53. if (weight > 0) {
  54. //请求时间
  55. long timestamp = invoker.getUrl().getParameter(TIMESTAMP_KEY, 0L);
  56. if (timestamp > 0L) {
  57. //处理时间,当前时间-invoker上线时间
  58. long uptime = System.currentTimeMillis() - timestamp;
  59. if (uptime < 0) {
  60. return 1;
  61. }
  62. //预热时间10分钟
  63. int warmup = invoker.getUrl().getParameter(WARMUP_KEY, DEFAULT_WARMUP);
  64. //上线时间小于预热时间,返回预热中的权重
  65. if (uptime > 0 && uptime < warmup) {
  66. weight = calculateWarmupWeight((int)uptime, warmup, weight);
  67. }
  68. }
  69. }
  70. //正常情况返回invoker权重
  71. return Math.max(weight, 0);
  72. }
  73. }

LeastActiveLoadBalance

官网对LeastActiveLoadBalance的解释是最小活跃数负载均衡,活跃调用数越小,表明该服务提供者效率越高,单位时间内可处理更多的请求,那么此时请求会优先分配给该服务提供者。

最小活跃数负载均衡算法思想

最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。每个服务提供者会对应着一个活跃数active,初始情况下所有服务提供者的active为0,每当收到一个请求,对应的服务提供者的active会加 1,处理完请求后,active会减 1。如果服务提供者性能较好,处理请求的效率就越高,那么active也会下降的越快。因此可以给这样的服务提供者优先分配请求。除了最小活跃数,LeastActiveLoadBalance在实现上还引入了权重值。所以准确的来说LeastActiveLoadBalance是基于加权最小活跃数算法实现的。

  1. @Override
  2. protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
  3. // 服务提供者列表的长度
  4. int length = invokers.size(); // Number of invokers
  5. // 最活跃初始值是-1
  6. int leastActive = -1; // The least active value of all invokers
  7. //具有相同的最小活动值的调用程序的数量(leastActive)
  8. int leastCount = 0; // The number of invokers having the same least active value (leastActive)
  9. int[] leastIndexs = new int[length]; // The index of invokers having the same least active value (leastActive)
  10. // 权重和
  11. int totalWeight = 0; // The sum of with warmup weights
  12. //初始值 用于比较
  13. int firstWeight = 0; // Initial value, used for comparision
  14. // 每个invoker是否是相同的权重?
  15. boolean sameWeight = true; // Every invoker has the same weight value?
  16. for (int i = 0; i < length; i++) {
  17. Invoker<T> invoker = invokers.get(i);
  18. // 获取当前这个invoker并发数
  19. int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive(); // Active number
  20. // 计算权重值
  21. int afterWarmup = getWeight(invoker, invocation); // Weight
  22. // 第一个元素的后 或者 当前invoker并发数 小于 最小并发数(初始值是-1)
  23. if (leastActive == -1 || active < leastActive) { // Restart, when find a invoker having smaller least active value.
  24. // 记录leastActive 为当前的活跃数
  25. leastActive = active; // Record the current least active value
  26. //重置最小计数,基于当前最小计数重新计数
  27. leastCount = 1; // Reset leastCount, count again based on current leastCount
  28. //在0下标出放入这个索引
  29. leastIndexs[0] = i; // Reset
  30. // 总权重就是 当前invoker的权重
  31. totalWeight = afterWarmup; // Reset
  32. //第一个权重
  33. firstWeight = afterWarmup; // Record the weight the first invoker
  34. sameWeight = true; // Reset, every invoker has the same weight value?
  35. } else if (active == leastActive) {
  36. // 当前invoker的活跃数 与 leastActive相等
  37. // If current invoker's active value equals with leaseActive, then accumulating.
  38. // 记录索引位置,具有相同最小活跃数的计数器 +1
  39. leastIndexs[leastCount++] = i; // Record index number of this invoker
  40. //总权重 = 总权重+当前权重
  41. totalWeight += afterWarmup; // Add this invoker's weight to totalWeight.
  42. // If every invoker has the same weight?
  43. if (sameWeight && i > 0
  44. && afterWarmup != firstWeight) {
  45. sameWeight = false;
  46. }
  47. }
  48. }
  49. // assert(leastCount > 0)
  50. if (leastCount == 1) {//如果我们恰好有一个调用程序具有最少的活动值,那么直接返回这个调用程序。
  51. // If we got exactly one invoker having the least active value, return this invoker directly.
  52. return invokers.get(leastIndexs[0]);
  53. }
  54. // -----------------------------------------------------------------------------------------------------------
  55. // 如果每个invoker有不同的权重 && totalWeight > 0
  56. if (!sameWeight && totalWeight > 0) {
  57. // If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on totalWeight.
  58. // 在totalWeight 范围内随机一个值
  59. int offsetWeight = random.nextInt(totalWeight) + 1;
  60. // Return a invoker based on the random value.
  61. for (int i = 0; i < leastCount; i++) {
  62. // 获取i位置的那个最小活跃 在invokers 里面的位置信息
  63. int leastIndex = leastIndexs[i];
  64. //offsetWeight - leastIndex 位置invoker的权重
  65. offsetWeight -= getWeight(invokers.get(leastIndex), invocation);
  66. // offsetWeight 小于0的话
  67. if (offsetWeight <= 0)
  68. // 返回这个位置的这个
  69. return invokers.get(leastIndex);
  70. }
  71. }
  72. // 具有相同权重或者是 总权重=0 的话就均匀返回
  73. // If all invokers have the same weight value or totalWeight=0, return evenly.
  74. return invokers.get(leastIndexs[random.nextInt(leastCount)]);
  75. }

ConsistentHashLoadBalance

在分布式系统中解决负载均衡问题的时侯使用一致性Hash算法将固定的一部分请求落在同一台机器上,每台服务器会固定的处理同一部分请求,来起到负载均衡的作用。provider挂掉的时候,会基于虚拟节点均匀分配剩余的流量,抖动不会太大。如果需要的不是随机负载均衡,要一类请求都到一个节点,那就走这个一致性Hash策略。

关于 dubbo 负载均衡策略更加详细的描述,可以查看官网 http://dubbo.apache.org/zh-cn/docs/source_code_guide/loadbalance.html 。

算法思想

普通的余数的hash(hash(key)%机器数)算法伸缩性很差,每当新增或者下线机器的时候,某个key与机器的映射会大量的失效,一致性hash则利用hash环对其进行了改进。比如我现在有4台服务器,他们对应的ip地址分别是ip1,ip2,ip3,ip4,通过计算这4个ip的hash值(这里假设hash(ip4)> hash(ip3)>hash(ip2)>hash(ip1)),然后按照hash值的大小顺时针分布到hash环上。

 从0开始然后按照4个ip的hash值大小顺时针方向(这个趋向正无穷大)散落在环上(这里假设hash(ip4)> hash(ip3)>hash(ip2)>hash(ip1)),当用户调用请求打过来的时候,计算用户某个参数的hash(key)值,比如用户u1的key的hash值正好在hash(ip3)值 与hash(2)值之间,这时候u1的请求就要交给hash(ip3)也就是ip3的这台机器处理,当ip3的机器挂了的时候,hash环是这样分布的:

这时u1的请求就会被重新分配到ip4的机器上 。

ConsistentHashLoadBalance继承AbstractLoadBalance抽象类,重写doSelect方法

  1. @Override
  2. protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
  3. // 获取方法名
  4. String methodName = RpcUtils.getMethodName(invocation);
  5. // 拼接key, 接口全类名.方法名
  6. String key = invokers.get(0).getUrl().getServiceKey() + "." + methodName;
  7. //根据invokers 计算个hashcode
  8. int identityHashCode = System.identityHashCode(invokers);
  9. // 根据key 从缓存中获取ConsistentHashSelector
  10. ConsistentHashSelector<T> selector = (ConsistentHashSelector<T>) selectors.get(key);
  11. // selector==null 或者是 selector的hashcode != 现在算出来的,说明这个invokers 变了
  12. if (selector == null || selector.identityHashCode != identityHashCode) {
  13. // 创建ConsistentHashSelector 放入缓存中
  14. selectors.put(key, new ConsistentHashSelector<T>(invokers, methodName, identityHashCode));
  15. // 获取新的selector
  16. selector = (ConsistentHashSelector<T>) selectors.get(key);
  17. }
  18. // 是用selector 进行选择
  19. return selector.select(invocation);
  20. }

首先是拼装key= 接口全类名.方法名,计算invokers的hash值,通过key从selectors缓存中获取对应的值ConsistentHashSelector,如果selector是null或者不一致(说明invokers集合中的invoker有变动,有下线或者上线情况,导致算出来的hash值与之前存的hash值不一致)就新new ConsistentHashSelector然后塞到selectors缓存中,然后调用当前selector的select(invocation)方法。ConsistentHashSelector 的构造方法:

  1. ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) {
  2. // 虚拟的invoker
  3. this.virtualInvokers = new TreeMap<Long, Invoker<T>>();
  4. // hashcode
  5. this.identityHashCode = identityHashCode;
  6. // 获取url
  7. URL url = invokers.get(0).getUrl();
  8. //获取hash.nodes ,缺省是160 每个实例节点的个数
  9. this.replicaNumber = url.getMethodParameter(methodName, "hash.nodes", 160);
  10. // 获取hash.arguments 缺省是0 然后进行切割
  11. String[] index = Constants.COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, "hash.arguments", "0"));
  12. argumentIndex = new int[index.length];
  13. for (int i = 0; i < index.length; i++) {
  14. argumentIndex[i] = Integer.parseInt(index[i]);
  15. }
  16. for (Invoker<T> invoker : invokers) {
  17. // 获取地址
  18. String address = invoker.getUrl().getAddress();
  19. for (int i = 0; i < replicaNumber / 4; i++) {
  20. byte[] digest = md5(address + i);
  21. for (int h = 0; h < 4; h++) {
  22. long m = hash(digest, h);//计算位置
  23. virtualInvokers.put(m, invoker);
  24. }
  25. }
  26. }
  27. }

先创建virtualInvokers存储虚拟节点的treemap,获取属性hash.nodes的值,缺省是160(每个invoker的节点个数默认160个),获取hash.arguments属性值,缺省是0(要使用哪个位置的参数,可以是多个用,逗号隔开,默认是使用第一个参数)。然后为每个invoker根据其ip+port生成replicaNumber个的节点(生成虚拟节点),塞到virtualInvokers的treemap中,key就是算出来的hash值,value就是invoker,TreeMap是按照key的值从小到大排序的。ConsistentHashSelector 的select(inv)方法:

  1. public Invoker<T> select(Invocation invocation) {
  2. // 将参数转成key
  3. String key = toKey(invocation.getArguments());
  4. byte[] digest = md5(key);
  5. return selectForKey(hash(digest, 0));
  6. }
  7. private String toKey(Object[] args) {
  8. StringBuilder buf = new StringBuilder();
  9. for (int i : argumentIndex) {
  10. if (i >= 0 && i < args.length) {
  11. buf.append(args[i]);// 参数
  12. }
  13. }
  14. return buf.toString();
  15. }
  16. private Invoker<T> selectForKey(long hash) {//tailMap 是返回键值大于或等于key的那部分 ,然后再取第一个
  17. Map.Entry<Long, Invoker<T>> entry = virtualInvokers.tailMap(hash, true).firstEntry();
  18. if (entry == null) {//如果没有取到的话就说明hash就是最大的了,下面那个就是 treemap 第一个了
  19. entry = virtualInvokers.firstEntry();
  20. }// 返回对应的那个invoker
  21. return entry.getValue();
  22. }
  23. private long hash(byte[] digest, int number) {
  24. return (((long) (digest[3 + number * 4] & 0xFF) << 24)
  25. | ((long) (digest[2 + number * 4] & 0xFF) << 16)
  26. | ((long) (digest[1 + number * 4] & 0xFF) << 8)
  27. | (digest[number * 4] & 0xFF))
  28. & 0xFFFFFFFFL;
  29. }
  30. private byte[] md5(String value) {
  31. MessageDigest md5;
  32. try {
  33. md5 = MessageDigest.getInstance("MD5");
  34. } catch (NoSuchAlgorithmException e) {
  35. throw new IllegalStateException(e.getMessage(), e);
  36. }
  37. md5.reset();
  38. byte[] bytes;
  39. try {
  40. bytes = value.getBytes("UTF-8");
  41. } catch (UnsupportedEncodingException e) {
  42. throw new IllegalStateException(e.getMessage(), e);
  43. }
  44. md5.update(bytes);
  45. return md5.digest();
  46. }

select方法中先是利用调用参数生成key,看下toKey方法中,就是根据我们hash.arguments 参数取出对应位的参数,拼接成key。在使用md5对key计算,使用hash算法算出key对应的hash值。然后调用selectForKey 方法根据这个key的hash值找出对应的invoker。
这个selectForKey方法,virtualInvokers.tailMap(hash, true).firstEntry()找出对应的节点,tailMap方法返回键值大于或等于key的那部分,使用firstEntry方法获取这部分的第一个。如果获取的entry是null的话,说明这个hash值就是最大的了,要想找对应的invoker ,就要找TreeMap的第一个元素,然后返回这个invoker。dubbo的一致性hash算法到这就结束了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/blog/article/detail/41339
推荐阅读
相关标签
  

闽ICP备14008679号