书接上篇 负载均衡组件Ribbon核心-@LoadBalanced-上 我们讲完了理解@LoadBalanced注解的知识准备,接下来就是@LoadBalanced注解详解。
@LoadBalanced 注解功能起点来至LoadBalancerAutoConfiguration自动配置类,SpringBoot容器启动的时,根据加入的依赖(spring-web/spring-cloud-ribbon),LoadBalancerAutoConfiguration 配置类会被激活
- @Configuration(proxyBeanMethods = false)
- @ConditionalOnClass(RestTemplate.class)
- @ConditionalOnBean(LoadBalancerClient.class)
- @EnableConfigurationProperties(LoadBalancerRetryProperties.class)
- public class LoadBalancerAutoConfiguration {
-
- }
激活条件:
@ConditionalOnClass(RestTemplate.class):spring-web 依赖引入RestTemplate类
@ConditionalOnBean(LoadBalancerClient.class):要求容器内有LoadBalancerClient Bean实例
这是,肯定有朋友疑惑:@LoadBalanced功能起点不是这个配置类么,哪来LoadBalancerClient Bean实例。有疑惑是对的,@LoadBalanced 注解主宰的是Ribbon负载均衡功能,而不是整个Ribbon操作,LoadBalancerClient Bean 在Ribbon 加载前就已经配置被创建了。且看
这个就是Ribbon组件引入的启动器,里面有个Ribbion自动配置类:RibbonAutoConfiguration,看下源码
- @Configuration
- @Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
- @RibbonClients
- @AutoConfigureAfter(
- name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
- @AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
- AsyncLoadBalancerAutoConfiguration.class })
- @EnableConfigurationProperties({ RibbonEagerLoadProperties.class,
- ServerIntrospectorProperties.class })
- public class RibbonAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean(LoadBalancerClient.class)
- public LoadBalancerClient loadBalancerClient() {
- return new RibbonLoadBalancerClient(springClientFactory());
- }
-
- }
再看头顶贴上的注解:
- @AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
- AsyncLoadBalancerAutoConfiguration.class })
真相来了,RibbonAutoConfiguration 配置类的启动早于LoadBalancerAutoConfiguration,也就是说,RibbonAutoConfiguration 配置的LoadBalancerClient 实例在LoadBalancerAutoConfiguration启动前就已经实例化好了。
当了解了LoadBalancerAutoConfiguration配置类如何进行激活,接下就看如何将负载均衡的能力集成在RestTemplate实例中。总结一下需要走下面3步:
1>RestTemplate 收集 2>RestTemplate 定制器 3>RestTemplate拦截注入
我们从LoadBalancerAutoConfiguration配置类restTemplates集合属性发现端倪。
- public class LoadBalancerAutoConfiguration {
-
- @LoadBalanced
- @Autowired(required = false)
- private List
restTemplates = Collections.emptyList(); -
- ...
- }
从源码上看,Spring容器会收集所有RestTemplate实例,并注入restTemplates 中。此时问题来了,它怎么知道哪些RestTemplate 类需要添加负载均衡功能?这是就用到@Qualifier 知识点了(不了解朋友可以看上篇)。
先看@LoadBalanced注解的源码
- @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @Inherited
- @Qualifier
- public @interface LoadBalanced {
-
- }
发现它头上有个元注解:@Qualifier, 意味着@LoadBalanced 拥有@Qualifier 注解所有功能,
- @LoadBalanced
- @Autowired(required = false)
- private List
restTemplates = Collections.emptyList(); -
- -------------------------------------------------------------------------------
- //@LoadBalanced
- @Qualifier //等价
- @Autowired(required = false)
- private List
restTemplates = Collections.emptyList();
很明显,它用上了@Qualifier 注解 Bean筛选标记功能。
步骤1完成后,可以得到待加负载均衡逻辑的restTemplates 集合。接下来就是集成了,Spring提供了XxxCustomizer类实现实例定制,下面是RestTemplate定制器接口源码
- public interface RestTemplateCustomizer {
-
- void customize(RestTemplate restTemplate);
-
- }
接口只有一个方法:customize, 参数为RestTemplate,很明显看出它就用来改造RestTemplate 实例的。
继续往下,LoadBalancerAutoConfiguration配置类下有个LoadBalancerInterceptorConfig 静态内部类
- public class LoadBalancerAutoConfiguration {
- @Configuration(proxyBeanMethods = false)
- @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
- static class LoadBalancerInterceptorConfig {
- ....
-
- @Bean
- @ConditionalOnMissingBean
- public RestTemplateCustomizer restTemplateCustomizer(
- final LoadBalancerInterceptor loadBalancerInterceptor) {
- return restTemplate -> {
- List
list = new ArrayList<>( - restTemplate.getInterceptors());
- list.add(loadBalancerInterceptor);
- restTemplate.setInterceptors(list);
- };
- }
-
- }
- }
- }
类内部,定义了用于构建RestTemplateCustomizer 定制器实例方法:restTemplateCustomizer(loadBalancerInterceptor),参数是loadBalancerInterceptor 拦截器。后续将该拦截器添加到restTemplate 实例中。
- list.add(loadBalancerInterceptor);
- restTemplate.setInterceptors(list);
定制逻辑完成,接下来回答2个问题,RestTemplate 类负载均衡逻辑就通了
问题1:RestTemplate 定制器怎么执行的?
问题2:LoadBalancerInterceptor 拦截器做啥逻辑?
这里先回答问题1,问题2在步骤3详解
RestTemplate 定制器使用 SmartInitializingSingleton 接口 (不理解/忘记可以看上篇)实现调用。
当Spring容器所有单例Bean实例初始化成功之后,实现SmartInitializingSingleton 接口Bean实例afterSingletonsInstantiated 方法会被回调。
- public class LoadBalancerAutoConfiguration {
- ....
- @Bean
- public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
- final ObjectProvider
> restTemplateCustomizers) {
- return () -> restTemplateCustomizers.ifAvailable(customizers -> {
- for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
- for (RestTemplateCustomizer customizer : customizers) {
- customizer.customize(restTemplate);
- }
- }
- });
- }
- ....
- }
源码逻辑:收集容器中所有RestTemplateCustomizer 定制器,对步骤1收集到的LoadBalancerAutoConfiguration.this.restTemplates RestTemplate对象集合,一一定制。
还是LoadBalancerAutoConfiguration配置类下有个LoadBalancerInterceptorConfig 静态内部类
- public class LoadBalancerAutoConfiguration {
- @Configuration(proxyBeanMethods = false)
- @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
- static class LoadBalancerInterceptorConfig {
-
- @Bean
- public LoadBalancerInterceptor ribbonInterceptor(
- LoadBalancerClient loadBalancerClient,
- LoadBalancerRequestFactory requestFactory) {
- return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
- }
- ....
- }
- }
- }
类内部,定义ribbonInterceptor 实例方法,参数为loadBalancerClient,requestFactory ,目的地是创建LoadBalancerInterceptor 拦截器实例。
步骤2中问题:LoadBalancerInterceptor 拦截器做了啥操作?
上篇文章我们了解过ClientHttpRequestInterceptor拦截器,当RestTemplate 调用getForEntity/getForObject方法发起http请求时,会执行拦截逻辑,LoadBalancerInterceptor 拦截器就是ClientHttpRequestInterceptor 实现类
- public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
- ....
- @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);
- return this.loadBalancer.execute(serviceName,
- this.requestFactory.createRequest(request, body, execution));
- }
-
- }
综上3个步骤,得出结论:
当贴有@LoadBalanced 注解的RestTemplate发起getForEntity/getForObject 会执行RestTemplateCustomizer 定制器绑定LoadBalancerInterceptor 拦截器 intercept 方法,然后返回ClientHttpResponse 结果。
到这,肯定有好学的小伙伴问,最后一步的负载均衡怎么调用啊?这个问题已经超出本篇文章的讨论了,如果想深究的朋友,还可以继续:
- @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);
- return this.loadBalancer.execute(serviceName,
- this.requestFactory.createRequest(request, body, execution));
- }
LoadBalancerInterceptor 拦截器 intercept 方法执行,调用了this.loadBalancer.execute 实现远程服务调用,负载均衡的调用效果,就由这个方法完成。再深入,就进到:
- public class RibbonLoadBalancerClient implements LoadBalancerClient {
-
- public
T execute(String serviceId, LoadBalancerRequest request, Object hint) - throws IOException {
- ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
- Server server = getServer(loadBalancer, hint);
- 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);
- }
- }
最核心逻辑:
Server server = getServer(loadBalancer, hint);
- protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
- if (loadBalancer == null) {
- return null;
- }
- // Use 'default' on a null hint, or just pass it on?
- return loadBalancer.chooseServer(hint != null ? hint : "default");
- }
这里的chooseServer方法,就是根据Ribbon配置的负载均衡策略来调用服务了,默认是
- public class ZoneAwareLoadBalancer
extends Server> extends DynamicServerListLoadBalancer { -
- public Server chooseServer(Object key) {
- ...
- }
- }
好了,到这,就真的结束了~
看文字不过瘾可以切换视频版:SpringCloud Alibaba 极简入门