1. Ribbon 负债均衡策略


命名 描述
RandomRule 随机策略 随机选择server
RoundRobinRule 轮询策略 按照顺序选择server(ribbon默认策略)
RetryRule 重试策略 在一个配置时间段内,当选择server不成功,则一直尝试选择一个可用的server
BestAvailableRule 最低并发策略 逐个考察server,如果server断路器打开,则忽略,再选择其中并发链接最低的server
AvailabilityFilteringRule 可用过滤策略 过滤掉一直失败并被标记为circuit tripped的server,过滤掉那些高并发链接的server(active connections超过配置的阈值)
ResponseTimeWeightedRule 响应时间加权重策略 根据server的响应时间分配权重,响应时间越长,权重越低,被选择到的概率也就越低。响应时间越短,权重越高,被选中的概率越高,这个策略很贴切,综合了各种因素,比如:网络,磁盘,io等,都直接影响响应时间
ZoneAvoidanceRule 区域权重策略 综合判断server所在区域的性能,和server的可用性,轮询选择server并且判断一个AWS Zone的运行性能是否可用,剔除不可用的Zone中的所有server

2. 源码分析

负载均衡的策略的核心实现接口是IRule 接口中的choose()方法,不同的负载策略通过实现choose()方法来实现不同逻辑

  1. public interface IRule{
  2. /*
  3. * choose one alive server from lb.allServers or
  4. * lb.upServers according to key
  5. *
  6. * @return choosen Server object. NULL is returned if none
  7. * server is available
  8. */
  9. public Server choose(Object key);
  10. public void setLoadBalancer(ILoadBalancer lb);
  11. public ILoadBalancer getLoadBalancer();
  12. }

2.1. 随机策略

随机策略的实现类是RandomRule类,实现choose()方法

  1. public Server choose(ILoadBalancer lb, Object key) {
  2. // 传入的LoadBalancer是空,直接返回null
  3. if (lb == null) {
  4. return null;
  5. }
  6. Server server = null;
  7. // 如果没有获取到随机服务,一直循环直到获取到服务
  8. while (server == null) {
  9. // 线程如果被中断,返回null
  10. if (Thread.interrupted()) {
  11. return null;
  12. }
  13. // 所有已注册可用服务列表
  14. List<Server> upList = lb.getReachableServers();
  15. // 所有已注册服务列表
  16. List<Server> allList = lb.getAllServers();
  17. // 所有已注册服务数量
  18. int serverCount = allList.size();
  19. if (serverCount == 0) {
  20. /*
  21. * No servers. End regardless of pass, because subsequent passes
  22. * only get more restrictive.
  23. */
  24. return null;
  25. }
  26. // 关键方法,生成随机一个0--serverCount 之间的随机数作为下标
  27. int index = chooseRandomInt(serverCount);
  28. // 根据下表从可用服务列表中拿Server实例
  29. server = upList.get(index);
  30. // server 为空,这种情况是因为serverList在这个时候正在被修正
  31. if (server == null) {
  32. /*
  33. * The only time this should happen is if the server list were
  34. * somehow trimmed. This is a transient condition. Retry after
  35. * yielding.
  36. */
  37. // 通知线程调度器,让出当前CPU
  38. // 这里的yield,是和上面第10行代码配合使用的,这是一种防御性编程的体现
  39. Thread.yield();
  40. continue;
  41. }
  42. // 如果server是可用的直接返回
  43. if (server.isAlive()) {
  44. return (server);
  45. }
  46. // Shouldn't actually happen.. but must be transient or a bug.
  47. server = null;
  48. Thread.yield();
  49. }
  50. return server;
  51. }

生成随机数,使用ThreadLocalRandom 然后在当前的thread中通过nextInt传入了一个serverCount,然后就会返回0到serverCount中的任意一个值。

  1. // 生成随机数
  2. protected int chooseRandomInt(int serverCount) {
  3. return ThreadLocalRandom.current().nextInt(serverCount);
  4. }

2.2. 轮询策略

轮询策略实现类RoundRobinRule

  1. public Server choose(ILoadBalancer lb, Object key) {
  2. // 如果没有LoadBalancer直接返回null
  3. if (lb == null) {
  4. log.warn("no load balancer");
  5. return null;
  6. }
  7. Server server = null;
  8. int count = 0;
  9. // 循环获取可用服务,直到获取服务和获取次数在10次以下结束循环
  10. while (server == null && count++ < 10) {
  11. // 可用的注册服务
  12. List<Server> reachableServers = lb.getReachableServers();
  13. // 所有的注册服务
  14. List<Server> allServers = lb.getAllServers();
  15. // 可用服务数量
  16. int upCount = reachableServers.size();
  17. // 所有服务数量
  18. int serverCount = allServers.size();
  19. if ((upCount == 0) || (serverCount == 0)) {
  20. log.warn("No up servers available from load balancer: " + lb);
  21. return null;
  22. }
  23. // 获取轮询服务下标
  24. int nextServerIndex = incrementAndGetModulo(serverCount);
  25. server = allServers.get(nextServerIndex);
  26. // 服务为null时,让出CPU
  27. if (server == null) {
  28. /* Transient. */
  29. Thread.yield();
  30. continue;
  31. }
  32. // 校验服务可用时直接返回
  33. if (server.isAlive() && (server.isReadyToServe())) {
  34. return (server);
  35. }
  36. // Next.
  37. server = null;
  38. }
  39. // 利用计数器限制循环次数,如果10次还没有获取到可用服务,打印一行日志
  40. if (count >= 10) {
  41. log.warn("No available alive servers after 10 tries from load balancer: "
  42. + lb);
  43. }
  44. return server;
  45. }

生成轮询服务下标

  1. private int incrementAndGetModulo(int modulo) {
  2. // 自选的方式获取轮询下标
  3. for (;;) {
  4. // 原子操作,上一次被调用的服务下标
  5. int current = nextServerCyclicCounter.get();
  6. // 上一次访问机器的下标+1和服务器数量相除取余,得到当前的服务下标
  7. int next = (current + 1) % modulo;
  8. // 通过CAS对比替换上一次调用服务的值为next值
  9. if (nextServerCyclicCounter.compareAndSet(current, next))
  10. return next;
  11. }
  12. }