相关推荐recommended
Ribbon IPing机制源码探秘
作者:mmseoamin日期:2023-12-11

🍊 Java学习:社区快速通道


🍊 深入浅出RocketMQ设计思想:深入浅出RocketMQ设计思想


🍊 绝对不一样的职场干货:大厂最佳实践经验指南


📆 最近更新:2023年7月2日


🍊 点赞 👍 收藏 ⭐留言 📝 都是我最大的动力!


文章目录

  • IPing机制
  • 用时间换空间
  • Ribbon IPing机制源码探秘

    IPing机制

    Ribbon会主动判断服务节点的当前状态,决定是否可作为目标节点,只有当前可用的节点才会作为负载均衡器的目标节点。IPing有以下几个手段:

    • DummyPing:默认返回true,即认为所有节点都可用,这也是单独使用Ribbon时的默认模式
    • NIWSDiscoveryPing:借助Eureka服务发现机制获取节点状态,假如节点状态是UP则认为是可用状态
    • PingUrl:主动向服务节点发起一次http调用,如果对方有响应则认为节点可用
    • PingConstant:返回设置的常量值
    • NoOpPing:返回true

      假如服务节点搭载的服务本身就承载超高并发的情况下,那这种主动出击的IPing策略必然会大大增加服务节点的访问压力。

      Ribbon IPing是一个接口,可以通过实现该接口来自定义Ping机制。Ribbon IPing的实现类需要在配置文件或者代码中指定,例如:

      #单个服务设置
      [service-name]:
        ribbon:
          NFLoadBalancerPingClassName: com.netflix.loadbalancer.DummyPing
      

      public class MicroRibbonConfig {
        @Bean
        public IPing microIPing() {
          return new DummyPing();
        }
      }
      @RibbonClient(name = "micro-service", configuration = MicroRibbonConfig.class)
      public class RibbonClientConfig {
      }
      

      Ribbon IPing的作用是保证负载均衡器只选择可用的服务节点,提高系统的可靠性和性能。Ribbon IPing与Eureka结合使用时,可以实现自动化的服务发现和健康检查。

      用时间换空间

      在Ribbon这里,时间和空间经常要被换来换去,时间代表着接口响应时间(RT,Response Time),空间表示服务器的可用连接数。

      在Ribbon里有两个和时间与空间密切相关的负载均衡策略,BestAvailableRule(简称BA)和WeightedResponseTimeRule。他们都会选择压力较小的服务节点,但这两个策略的方向不同。BA会根据服务节点过去一段时间的请求数,选择并发量最小的机器(选择空间);WRT则是根据响应时间的统计结果,选择响应时间最快的服务(选择时间)。

      • 连接数敏感模型: 对响应时间较短,或RT和业务复杂度是非线性相关关系的接口,采用基于可用连接数的负载均衡策略更加合适。
      • RT敏感模型: 对重量级接口,尤其是根据参数不同会导致系统资源使用率浮动较大的接口(RT与业务复杂度线性相关),建议采用基于响应时间的负载均衡策略。

        总结一下就是轻量级接口选空间(BestAvailableRule)、重量级接口选时间(WeightedResponseTimeRule)

        Ribbon IPing机制源码探秘

        IPing接口的定义如下:

        public interface IPing {
            public boolean isAlive(Server server);
        }
        

        看一下它的几个实现类:

        public class DummyPing extends AbstractLoadBalancerPing {
            public DummyPing() {
            }
            public boolean isAlive(Server server) {
                return true;
            }
            @Override
            public void initWithNiwsConfig(IClientConfig clientConfig) {
            }
        }
        

        什么都没发生,直接返回true

        public class NoOpPing implements IPing {
            @Override
            public boolean isAlive(Server server) {
                return true;
            }
        }
        

        也是直接返回true


        PingUrl的内容比较丰富,关注一下isAlive方法:

        public boolean isAlive(Server server) {
            String urlStr = "";
            if (this.isSecure) {
                urlStr = "https://";
            } else {
                urlStr = "http://";
            }
            urlStr = urlStr + server.getId();
            urlStr = urlStr + this.getPingAppendString();
            boolean isAlive = false;
            HttpClient httpClient = new DefaultHttpClient();
            HttpUriRequest getRequest = new HttpGet(urlStr);
            String content = null;
            try {
                HttpResponse response = httpClient.execute(getRequest);
                content = EntityUtils.toString(response.getEntity());
                isAlive = response.getStatusLine().getStatusCode() == 200;
                if (this.getExpectedContent() != null) {
                    LOGGER.debug("content:" + content);
                    if (content == null) {
                        isAlive = false;
                    } else if (content.equals(this.getExpectedContent())) {
                        isAlive = true;
                    } else {
                        isAlive = false;
                    }
                }
            } catch (IOException var11) {
                var11.printStackTrace();
            } finally {
                getRequest.abort();
            }
            return isAlive;
        }
        

        如果是安全协议则使用https,不是安全的则使用http,拼接的时候先加上一个server.getId(),再加上一个this.getPingAppendString()

        HttpClient httpClient = new DefaultHttpClient();
        HttpUriRequest getRequest = new HttpGet(urlStr);
        

        构造一个http请求来判断是否是up状态

        if (this.getExpectedContent() != null)
        

        如果有返回的期望值,则需要实际返回的数据等于期望值才证明是服务节点是up的


        NIWSDiscoveryPing实现类:

        public boolean isAlive(Server server) {
            boolean isAlive = true;
            if (server!=null && server instanceof DiscoveryEnabledServer){
                   DiscoveryEnabledServer dServer = (DiscoveryEnabledServer)server;                
                   InstanceInfo instanceInfo = dServer.getInstanceInfo();
                   if (instanceInfo!=null){                    
                       InstanceStatus status = instanceInfo.getStatus();
                       if (status!=null){
                           isAlive = status.equals(InstanceStatus.UP);
                       }
                   }
               }
            return isAlive;
        }
        

        只有server的类型是DiscoveryEnabledServer才将其转换成自己需要的类型,然后获得instanceInfo。

        这里InstanceStatus是依靠eureka的服务发现从服务注册中心拉取到的,服务发现并不能即使反映所有服务器的状态变化,因为是客户端发起的,所以有延迟。