Spring Cloud之@LoadBalanced注解

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
免费领取 2000+ 道 Java 面试题

Spring Cloud的commons模块提供了一个@LoadBalanced注解,方便我们对RestTemplate添加一个LoadBalancerClient,以实现客户端负载均衡。通过源码可以发现这是一个标记注解:

    /** * Annotation to mark a RestTemplate bean to be configured * to use a LoadBalancerClient * @author Spencer Gibb */
    @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Qualifier
    public @interface LoadBalanced {
    }

因为Spring Boot的自动配置机制,我们可以在这个包下看到一个LoadBalancerAutoConfiguration自动配置类:

    /** * Auto configuration for Ribbon (client side load * balancing). * * @author Spencer Gibb * @author Dave Syer * @author Will Tran */
    @Configuration
    @ConditionalOnClass(RestTemplate.class)
    @ConditionalOnBean(LoadBalancerClient.class)
    @EnableConfigurationProperties(LoadBalancerRetryProperties.class)
    public class LoadBalancerAutoConfiguration {

        @LoadBalanced
        @Autowired(required = false)
        private List<RestTemplate> restTemplates = Collections.emptyList();//这里持有@LoadBalanced标记的RestTemplate实例

        @Bean
        public SmartInitializingSingleton loadBalancedRestTemplateInitializer(
                final List<RestTemplateCustomizer> customizers) {
            return new SmartInitializingSingleton() {
                //为restTemplate添加定制
                @Override
                public void afterSingletonsInstantiated() {
                    for (RestTemplate restTemplate :LoadBalancerAutoConfiguration.this.restTemplates) {
                        for (RestTemplateCustomizer customizer 
                        : customizers) {
                            customizer.customize(restTemplate);
                        }
                    }
                }
            };
        }

        @Autowired(required = false)
        private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

        @Bean
        @ConditionalOnMissingBean
        public LoadBalancerRequestFactory loadBalancerRequestFactory(
                LoadBalancerClient loadBalancerClient) {
            return new LoadBalancerRequestFactory(loadBalancerClient, transformers);
        }

        @Configuration
        @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")//不存在RetryTemplate时生效
        static class LoadBalancerInterceptorConfig {
            @Bean
            public LoadBalancerInterceptor ribbonInterceptor(
                    LoadBalancerClient loadBalancerClient,
                    LoadBalancerRequestFactory requestFactory) {
                return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
            }

            @Bean
            @ConditionalOnMissingBean
            public RestTemplateCustomizer restTemplateCustomizer(
                    final LoadBalancerInterceptor loadBalancerInterceptor) {
                return new RestTemplateCustomizer() {
                //为restTemplate添加拦截器
                    @Override
                    public void customize(RestTemplate restTemplate){
                    List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());
                        list.add(loadBalancerInterceptor);
                        restTemplate.setInterceptors(list);
                    }
                };
            }
        }
    //以下针对classpath存在RetryTemplate.class的情况配置,先忽略
        @Configuration
        @ConditionalOnClass(RetryTemplate.class)
        public static class RetryInterceptorAutoConfiguration {
            @Bean
            @ConditionalOnMissingBean
            public RetryLoadBalancerInterceptor ribbonInterceptor() {
    //...
            }

            @Bean
            @ConditionalOnMissingBean
            public RestTemplateCustomizer restTemplateCustomizer(
                    final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
                return new RestTemplateCustomizer() {
                    @Override
                    public void customize(RestTemplate restTemplate) {
                        List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                                restTemplate.getInterceptors());
                        list.add(loadBalancerInterceptor);
                        restTemplate.setInterceptors(list);
                    }
                };
            }
        }
    }

先忽略存在RetryTemplate的情况,我们看到添加的拦截器是LoadBalancerInterceptor:

    /** * @author Spencer Gibb * @author Dave Syer * @author Ryan Baxter * @author William Tran */
    public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

        private LoadBalancerClient loadBalancer;
        private LoadBalancerRequestFactory requestFactory;

        public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
            this.loadBalancer = loadBalancer;
            this.requestFactory = requestFactory;
        }

        public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
            // for backwards compatibility
            this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
        }

        @Override
        public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
                final ClientHttpRequestExecution execution) throws IOException {
            final URI originalUri = request.getURI();
            String serviceName = originalUri.getHost();
            Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
    //主要的拦截处理,就是在这里通过LoadBalancerClient执行具体的请求发送
            return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
        }
    }

LoadBalancerClient:

    /** * Represents a client side load balancer * @author Spencer Gibb */
    public interface LoadBalancerClient extends ServiceInstanceChooser {

        <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

        <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;

        URI reconstructURI(ServiceInstance instance, URI original);
    }

我们可以找到Spring Cloud的netflix-core模块提供了一个LoadBalancerClient的实现类RibbonLoadBalancerClient

    /** * @author Spencer Gibb * @author Dave Syer * @author Ryan Baxter */
    public class RibbonLoadBalancerClient implements LoadBalancerClient {

        private SpringClientFactory clientFactory;

        public RibbonLoadBalancerClient(SpringClientFactory clientFactory) {
            this.clientFactory = clientFactory;
        }

        @Override
        public URI reconstructURI(ServiceInstance instance, URI original) {
            Assert.notNull(instance, "instance can not be null");
            String serviceId = instance.getServiceId();
            RibbonLoadBalancerContext context = this.clientFactory
                    .getLoadBalancerContext(serviceId);
            Server server = new Server(instance.getHost(), instance.getPort());
            IClientConfig clientConfig = clientFactory.getClientConfig(serviceId);
            ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
            URI uri = RibbonUtils.updateToHttpsIfNeeded(original, clientConfig,
                    serverIntrospector, server);
            return context.reconstructURIWithServer(server, uri);
        }

    //通过LoadBalancer找到service_id对应的ServiceInstance
        @Override
        public ServiceInstance choose(String serviceId) {
            Server server = getServer(serviceId);
            if (server == null) {
                return null;
            }
            return new RibbonServer(serviceId, server, isSecure(server, serviceId),
                    serverIntrospector(serviceId).getMetadata(server));
        }

        @Override
        public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
            ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
            Server server = getServer(loadBalancer);
            if (server == null) {
                throw new IllegalStateException("No instances available for " + serviceId);
            }
            RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
                    serviceId), serverIntrospector(serviceId).getMetadata(server));

            return execute(serviceId, ribbonServer, request);
        }

        @Override
        public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
            Server server = null;
            if(serviceInstance instanceof RibbonServer) {
                server = ((RibbonServer)serviceInstance).getServer();
            }
            if (server == null) {
                throw new IllegalStateException("No instances available for " + serviceId);
            }

            RibbonLoadBalancerContext context = this.clientFactory
                    .getLoadBalancerContext(serviceId);
            RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);

            try {
                T returnVal = request.apply(serviceInstance);
                statsRecorder.recordStats(returnVal);
                return returnVal;
            }
            // catch IOException and rethrow so RestTemplate behaves correctly
            catch (IOException ex) {
                statsRecorder.recordStats(ex);
                throw ex;
            }
            catch (Exception ex) {
                statsRecorder.recordStats(ex);
                ReflectionUtils.rethrowRuntimeException(ex);
            }
            return null;
        }

        private ServerIntrospector serverIntrospector(String serviceId) {
            ServerIntrospector serverIntrospector = this.clientFactory.getInstance(serviceId,
                    ServerIntrospector.class);
            if (serverIntrospector == null) {
                serverIntrospector = new DefaultServerIntrospector();
            }
            return serverIntrospector;
        }

        protected Server getServer(String serviceId) {
            return getServer(getLoadBalancer(serviceId));
        }

        protected Server getServer(ILoadBalancer loadBalancer) {
            if (loadBalancer == null) {
                return null;
            }
            //实际负载均衡的地方
            return loadBalancer.chooseServer("default"); 
            // TODO: better handling of key
        }

        protected ILoadBalancer getLoadBalancer(String serviceId) {
            return this.clientFactory.getLoadBalancer(serviceId);
        }

        public static class RibbonServer implements ServiceInstance {
            //...
        }

    }

我们看到实际负载均衡的地方是netflix的ILoadBalancer来实现的。

20191123100144\_1.png

Spring Cloud默认使用的是ZoneAwareLoadBalancer。这个解析就需要另外再开一篇。上面主要是了解了一下Spring Cloud的@LoadBalanced注解让我们可以很容易的通过ribbon实现客户端的负载均衡功能。


来源:http://ddrv.cn/a/88268

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring Cloud之@LoadBalanced注解

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏