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

@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 webClientBuilders = Collections.emptyList();

	public List 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 restTemplates = Collections.emptyList();

		@Bean
		public SmartInitializingSingleton loadBalancedAsyncRestTemplateInitializer(
				final List 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 restTemplates = Collections.emptyList();

	@Bean
	public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
			final ObjectProvider> restTemplateCustomizers) {
		return () -> restTemplateCustomizers.ifAvailable(customizers -> {
            for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
                for (RestTemplateCustomizer customizer : customizers) {
                    customizer.customize(restTemplate);
                }
            }
        });
	}

	@Autowired(required = false)
	private List 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 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 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 restTemplates = Collections.emptyList();

    @Bean
    public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
            final ObjectProvider> restTemplateCustomizers) {
        return new SmartInitializingSingleton() {
            @Override
            public void afterSingletonsInstantiated() {
                restTemplateCustomizers.ifAvailable(new Consumer>() {
                    @Override
                    public void accept(List customizers) {
                        for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
                            for (RestTemplateCustomizer customizer : customizers) {
                                customizer.customize(restTemplate);
                            }
                        }
                    }
                });
            }
        };
    }

    @Autowired(required = false)
    private List 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 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 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 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 users = Collections.emptyList();
 
    @Autowired(required = false)
    private Map userMap = new HashMap<>();
 
    @RequestMapping("/list")
    public Object listUsers() {
        return users;
    }
    @RequestMapping("/map")
    public Object mapUsers() {
        return userMap;
    }
}

Controller通过以下代码:

    @Autowired(required = false)
    private List users = Collections.emptyList();
 
    @Autowired(required = false)
    private Map 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 users = Collections.emptyList();
 
    @Autowired(required = false)
    @Qualifier("invalid")
    private Map userMap = new HashMap<>();
 
    @RequestMapping("/list")
    public Object listUsers() {
        return users;
    }
    @RequestMapping("/map")
    public Object mapUsers() {
        return userMap;
    }
}

那么所有标注了@Qualifier("valid")的user bean都会自动注入到List users中去(本例是user1),所有标注了@Qualifier("invalid")的user bean都会自动注入到Map 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 users打上@LoadBalanced注解:

    @Autowired(required = false)
    @LoadBalanced
    private List 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,还是集合类型 List, 如果是Map,容器中存在@Qulifier一个,则直接注入;如果有多个,看是否有beanid与字段name相同的,否则抛出异常 expected single matching bean but found n 。如果是List 则将容器所有的@Qulifier注入到集合中。

 

你可能感兴趣的:(架构,Java基础,tl微服务专题)