美文网首页工作生活
Ribbon源码解析

Ribbon源码解析

作者: 我是嘻哈大哥 | 来源:发表于2019-07-03 22:14 被阅读0次

    1.简单使用
    1.1.在启动类中,声明一个RestTemplate的bean,用@LoadBalanced注解修饰:

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    

    1.2.在进行http调用的地方,通过@Autowired方式,注入RestTemplate,再通过restTemplate调用。调用时,写服务名就可以,比如按照下面这种方式调用。

      return restTemplate.getForObject("http://MICOSERICE-USER/user/getuser",ResponseDto.class);
    

    通过这两步操作,进行调用时,就可以通过服务名,得到多个服务实例的信息,通过负载均衡的方式进行调用。

    从上面的步骤看来,使用负载均衡的开发量还是挺简单的,不过为什么在restTemplate上加个注解,通过restTemplate进行调用,就可以做到负载均衡呢,这里就需要研究下源码才能知道原因了。

    2.源码分析
    进入到@LoadBalanced注解,如下:

    @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Qualifier
    public @interface LoadBalanced {
    }
    

    这个也看不出啥,在源码中LoadBalancerAutoConfiguration中使用了这个注解,如下:

        @LoadBalanced
        @Autowired(
            required = false
        )
        private List<RestTemplate> restTemplates = Collections.emptyList(); //定义了一个restTemplates列表
    

    在Spring注入当前类的loadBalancedRestTemplateInitializer的Bean时有如下方法

        @Bean
        public SmartInitializingSingleton loadBalancedRestTemplateInitializer(final List<RestTemplateCustomizer> customizers) {
            return new SmartInitializingSingleton() {
                public void afterSingletonsInstantiated() {
                    Iterator var1 = LoadBalancerAutoConfiguration.this.restTemplates.iterator();
    
                    while(var1.hasNext()) {
                        RestTemplate restTemplate = (RestTemplate)var1.next();
                        Iterator var3 = customizers.iterator();
    
                        while(var3.hasNext()) {
                            RestTemplateCustomizer customizer = (RestTemplateCustomizer)var3.next();
                            customizer.customize(restTemplate);  //调用RestTemplateCustomizer的customize方法
                        }
                    }
    
                }
            };
        }
    
            @Bean
            @ConditionalOnMissingBean
            public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
                return new RestTemplateCustomizer() {
                    public void customize(RestTemplate restTemplate) {
                        List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
                        list.add(loadBalancerInterceptor);  //添加了一个loadBalancerInterceptor拦截器
                        restTemplate.setInterceptors(list);
                    }
                };
            }
    

    loadBalancerInterceptor拦截器定义的拦截方法如下:

        public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
            URI originalUri = request.getURI();  //获取url
            String serviceName = originalUri.getHost(); //获取host
            Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
            return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution)); //loadBalancer执行execute方法
        }
    

    loadBalancer执行execute方法如下:

        public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
            ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId); //根据serviceId获取loadBalancer
            Server server = this.getServer(loadBalancer); //获取server
            if (server == null) {
                throw new IllegalStateException("No instances available for " + serviceId);
            } else {
                RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
                return this.execute(serviceId, ribbonServer, request); //执行相应的请求
            }
        }
    

    其中getServer方法如下:

        protected Server getServer(ILoadBalancer loadBalancer) {
            return loadBalancer == null ? null : loadBalancer.chooseServer("default"); //选择server
        }
    

    这里默认执行的是ZoneAwareLoadBalancer下的chooseServer方法

        public Server chooseServer(Object key) {
            if (ENABLED.get() && this.getLoadBalancerStats().getAvailableZones().size() > 1) {
                Server server = null;
    
                try {
                    LoadBalancerStats lbStats = this.getLoadBalancerStats();
                    Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
                    logger.debug("Zone snapshots: {}", zoneSnapshot);
                    if (this.triggeringLoad == null) {
                        this.triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty("ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".triggeringLoadPerServerThreshold", 0.2D);
                    }
    
                    if (this.triggeringBlackoutPercentage == null) {
                        this.triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty("ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".avoidZoneWithBlackoutPercetage", 0.99999D);
                    }
    
                    Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, this.triggeringLoad.get(), this.triggeringBlackoutPercentage.get());
                    logger.debug("Available zones: {}", availableZones);
                    if (availableZones != null && availableZones.size() < zoneSnapshot.keySet().size()) {
                        String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);
                        logger.debug("Zone chosen: {}", zone);
                        if (zone != null) {
                            BaseLoadBalancer zoneLoadBalancer = this.getLoadBalancer(zone);
                            server = zoneLoadBalancer.chooseServer(key); //选择server
                        }
                    }
                } catch (Exception var8) {
                    logger.error("Error choosing server using zone aware logic for load balancer={}", this.name, var8);
                }
    
                if (server != null) {
                    return server;
                } else {
                    logger.debug("Zone avoidance logic is not invoked.");
                    return super.chooseServer(key);
                }
            } else {
                logger.debug("Zone aware logic disabled or there is only one zone");
                return super.chooseServer(key);
            }
        }
    
        public Server chooseServer(Object key) {
            if (this.counter == null) {
                this.counter = this.createCounter();
            }
    
            this.counter.increment();
            if (this.rule == null) {
                return null;
            } else {
                try {
                    return this.rule.choose(key); //根据规则选择Server,默认是RoundRobinRule,即轮询规则
                } catch (Exception var3) {
                    logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", new Object[]{this.name, key, var3});
                    return null;
                }
            }
        }
    

    IRule的实现有以下:

    AvailabilityFilteringRule过滤器规则
    BestAvailableRule 选择最小请求数
    ClientConfigEnabledRoundRobinRule 轮询
    RandomRule 随机选择一个server
    RoundRobinRule 轮询选择server
    RetryRule 根据轮询的方式重试
    WeightedResponseTimeRule 根据响应时间去分配一个weight ,weight越低,被选择的可能性就越低
    ZoneAvoidanceRule 根据server的zone区域和可用性来轮询选择
    

    相关文章

      网友评论

        本文标题:Ribbon源码解析

        本文链接:https://www.haomeiwen.com/subject/smpchctx.html