springcloud ribbon的负载均衡注解 @LoadBalanced的实现原理(@Qulifier注解的使用)

mac2024-03-25  27

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

如上,在使用springcloud ribbon的rest服务客户端的时候,可给RestTemplate的bean添加@LoadBalanced注解,可使该RestTemplate在请求时拥有客户端负载均衡的能力。

以下是该注解功能来源分析:

一、@LoadBalanced的源码:

package org.springframework.cloud.client.loadbalancer; import org.springframework.beans.factory.annotation.Qualifier; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 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 { }

该注解的特殊之处在于使用了@Qulifier注解。

搜索@LoadBalanced注解的使用,springcloud源码中有三个类使用了该注解:

(由第三个的@ConditionalOnClass(RestTemplate.class)推断第三个才是此处有关的)

1、

@Configuration @ConditionalOnClass(WebClient.class) @ConditionalOnBean(LoadBalancerClient.class) public class ReactiveLoadBalancerAutoConfiguration { @LoadBalanced @Autowired(required = false) private List<WebClient.Builder> webClientBuilders = Collections.emptyList(); public List<WebClient.Builder> getBuilders() { return webClientBuilders; } …………

2、

/** * Auto configuration for Ribbon (client side load balancing). * * @author Rob Worsnop */ @Configuration @ConditionalOnBean(LoadBalancerClient.class) @ConditionalOnClass(AsyncRestTemplate.class) public class AsyncLoadBalancerAutoConfiguration { @Configuration static class AsyncRestTemplateCustomizerConfig { @LoadBalanced @Autowired(required = false) private List<AsyncRestTemplate> restTemplates = Collections.emptyList(); @Bean public SmartInitializingSingleton loadBalancedAsyncRestTemplateInitializer( final List<AsyncRestTemplateCustomizer> customizers) { return new SmartInitializingSingleton() { @Override public void afterSingletonsInstantiated() { for (AsyncRestTemplate restTemplate : AsyncRestTemplateCustomizerConfig.this.restTemplates) { for (AsyncRestTemplateCustomizer customizer : customizers) { customizer.customize(restTemplate); } } } }; } } …………

3

package org.springframework.cloud.client.loadbalancer; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.retry.backoff.BackOffPolicy; import org.springframework.retry.support.RetryTemplate; import org.springframework.web.client.RestTemplate; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Auto configuration for Ribbon (client side load balancing). * * @author Spencer Gibb * @author Dave Syer * @author Will Tran * @author Gang Li */ @Configuration @ConditionalOnClass(RestTemplate.class) @ConditionalOnBean(LoadBalancerClient.class) @EnableConfigurationProperties(LoadBalancerRetryProperties.class) public class LoadBalancerAutoConfiguration { @LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList(); @Bean public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated( final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) { return () -> restTemplateCustomizers.ifAvailable(customizers -> { 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") 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 restTemplate -> { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors());//把已有的拦截器列表取到。 list.add(loadBalancerInterceptor);//把负载均衡拦截器放进去。 restTemplate.setInterceptors(list);//把最新的拦截器列表放进去。 }; } } @Configuration @ConditionalOnClass(RetryTemplate.class) public static class RetryAutoConfiguration { @Bean @ConditionalOnMissingBean public LoadBalancedRetryFactory loadBalancedRetryFactory() { return new LoadBalancedRetryFactory() {}; } } @Configuration @ConditionalOnClass(RetryTemplate.class) public static class RetryInterceptorAutoConfiguration { @Bean @ConditionalOnMissingBean public RetryLoadBalancerInterceptor ribbonInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties, LoadBalancerRequestFactory requestFactory, LoadBalancedRetryFactory loadBalancedRetryFactory) { return new RetryLoadBalancerInterceptor(loadBalancerClient, properties, requestFactory, loadBalancedRetryFactory); } @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( final RetryLoadBalancerInterceptor loadBalancerInterceptor) { return restTemplate -> { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); }; } } }

以上第3个代码中的Lambda表达式,在idea中使用源码,用Alt+Enter快捷键将其还原之后的代码为下:

@Configuration @ConditionalOnClass(RestTemplate.class) @ConditionalOnBean(LoadBalancerClient.class) @EnableConfigurationProperties(LoadBalancerRetryProperties.class) public class LoadBalancerAutoConfiguration { @LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList(); @Bean public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated( final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) { return new SmartInitializingSingleton() { @Override public void afterSingletonsInstantiated() { restTemplateCustomizers.ifAvailable(new Consumer<List<RestTemplateCustomizer>>() { @Override public void accept(List<RestTemplateCustomizer> customizers) { 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") 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() { @Override public void customize(RestTemplate restTemplate) { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); } }; } } @Configuration @ConditionalOnClass(RetryTemplate.class) public static class RetryAutoConfiguration { @Bean @ConditionalOnMissingBean public LoadBalancedRetryFactory loadBalancedRetryFactory() { return new LoadBalancedRetryFactory() {}; } } @Configuration @ConditionalOnClass(RetryTemplate.class) public static class RetryInterceptorAutoConfiguration { @Bean @ConditionalOnMissingBean public RetryLoadBalancerInterceptor ribbonInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties, LoadBalancerRequestFactory requestFactory, LoadBalancedRetryFactory loadBalancedRetryFactory) { return new RetryLoadBalancerInterceptor(loadBalancerClient, properties, requestFactory, loadBalancedRetryFactory); } @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); } }; } } }

LoadBalancerAutoConfiguration类的代码中,用RestTempllate的拦截器,使用RestTemplateCustomizer对所有标注了@LoadBalanced的RestTemplate Bean添加了一个LoadBalancerInterceptor拦截器,而这个拦截器的作用就是对请求的URI进行转换获取到具体应该请求哪个服务实例ServiceInstance。

二、起作用的原理:

@LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList();

这段代码能将所有标注了@LoadBalanced的RestTemplate自动注入进来,在于@LoadBalanced注解上的@Qualifier注解。

@Qualifier注解和日常使用的@Autowired注解:

一)、日常使用很多都是用@Autowired来注入一个bean,其实@Autowired还可以注入List和Map,比如我定义两个Bean:

@Bean("user1") User user1() { return new User("1", "a"); } @Bean("user2")) User user2() { return new User("2", "b"); }

Controller:

@RestController public class MyController { @Autowired(required = false) private List<User> users = Collections.emptyList(); @Autowired(required = false) private Map<String,User> userMap = new HashMap<>(); @RequestMapping("/list") public Object listUsers() { return users; } @RequestMapping("/map") public Object mapUsers() { return userMap; } }

Controller通过以下代码:

@Autowired(required = false) private List<User> users = Collections.emptyList(); @Autowired(required = false) private Map<String,User> userMap = new HashMap<>();

可以自动将两个bean注入进来,当注入map的时候,map的key必须是String类型,然后bean name将作为map的key,本例,map中将有两个key分别为user1和user2,value分别为对应的User Bean实例。

访问http://localhost:8080/map:

{ "user1": { "id": "1", "name": "a" }, "user2": { "id": "2", "name": "b" } }

访问http://localhost:8080/list:

[ { "id": "1", "name": "a" }, { "id": "2", "name": "b" } ]

二)、然后我们给user1和user2分别打上@Qualifier修饰符:

@Bean("user1") @Qualifier("valid") User user1() { return new User("1", "a"); } @Bean("user2") @Qualifier("invalid") User user2() { return new User("2", "b"); }

将controller中的user list 和user map分别也打上@Qualifier修饰符:

@RestController public class MyController { @Autowired(required = false) @Qualifier("valid") private List<User> users = Collections.emptyList(); @Autowired(required = false) @Qualifier("invalid") private Map<String,User> userMap = new HashMap<>(); @RequestMapping("/list") public Object listUsers() { return users; } @RequestMapping("/map") public Object mapUsers() { return userMap; } }

那么所有标注了@Qualifier("valid")的user bean都会自动注入到List<user> users中去(本例是user1),所有标注了@Qualifier("invalid")的user bean都会自动注入到Map<String,User> userMap中去(本例是user2)。

访问http://localhost:8080/list:

[ { "id": "1", "name": "a" } ]

访问http://localhost:8080/map:

{ "user2": { "id": "2", "name": "b" } }

三)、看到这里我们可以理解@LoadBalanced的用处了,其实就是一个修饰符,和@Qualifier一样,比如我们给user1打上@LoadBalanced:

@Bean("user1") @LoadBalanced User user1() { return new User("1", "a"); } @Bean("user2") User user2() { return new User("2", "b"); }

然后controller中给List<User> users打上@LoadBalanced注解:

@Autowired(required = false) @LoadBalanced private List<User> users = Collections.emptyList();

访问http://localhost:8080/list:

[ { "id": "1", "name": "a" } ]

和@Qualifier注解效果一样,只有user1被注入进了List,user2没有修饰符,没有被注入进去。

另外当spring容器中有多个相同类型的bean的时候,可以通过@Qualifier来进行区分,以便在注入的时候明确表明你要注入具体的哪个bean,消除歧义。一般我们都是@Qulifier("asd")指定id注入; 如果@Qulifier不指定value,则就是注入@Bean标有@Qualifier的所有对象;但是这里又区分注入类型是Map<K,T>,还是集合类型 List<T>, 如果是Map<K,T>,容器中存在@Qulifier一个,则直接注入;如果有多个,看是否有beanid与字段name相同的,否则抛出异常 expected single matching bean but found n 。如果是List<T> 则将容器所有的@Qulifier注入到集合中。

 

最新回复(0)