@LoadBalanced注解与客户端负载均衡的实现

     在SC项目中,我们需要使用负载均衡方式的restTemplate 只需要在RestTemplate的Bean上加上@LoadBalanced注解即可,非常方便,这个是怎么做到的呢 (笔者之前也很好奇)

     首先我们看下@LoadBalanced定义

/**
 * 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 {
}

   从注释中就能看出是特意用来标记restTemplate使之配置使用LoadBalancerClient,而LoadBalancerClient又是什么呢

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);
}

LoandBalancerClient是一个接口,具体作用暂时不管,后面会重点分析,我们先来看 @LoadBalanced如何工作的

全局搜索下 @LoadBalanced 会发现有这样一个类 LoadBalancerAutoConfiguration,它有个属性restTemplates

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

 那么@LoadBalanced和@Autowried结合使用的效果呢,意思就是这里注入的RestTempate Bean是所有加有

@LoadBalanced注解标记的,听起来有点绕口,这里我直接给出我们项目中的使用方式

        @LoadBalanced
	@Qualifier("loadBalancedRestTemplate")
	@Bean(name = "loadBalancedRestTemplate")
	@ConditionalOnMissingBean(name = "loadBalancedRestTemplate")
	@ConditionalOnClass({ RestTemplate.class, HttpClient.class, RibbonLoadBalancerClient.class })
	public LoadBalancedRestTemplate myRestTemplate() {
		LoadBalancedRestTemplate restTemplate = new LoadBalancedRestTemplate(createFactory(), remoteClientProperties);
		this.setConverterList(restTemplate);
		List<ClientHttpRequestInterceptor> ins = restTemplate.getInterceptors();
		ins.add(new EyasHeaderInterceptor(securityProperties, getRequestSourceApp()));
		restTemplate.setInterceptors(ins);
		return restTemplate;
	}

我们定义了一个LoadBalancedRestTemplate bean 加上了@LoadBalanced注解,在restTemplates属性注入时候就会把

loadBalancedRestTemplate 注入进来,有什么用呢,接着看 LoadBalancerAutoConfiguration类

        @Bean
	public SmartInitializingSingleton loadBalancedRestTemplateInitializer(
			final List<RestTemplateCustomizer> customizers) {
		return new SmartInitializingSingleton() {
			@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")
	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);
				}
			};
		}
	}

这里可以看出,会对我们加上@LoadBalanced注解的bean 添加loadBalancerInterceptor拦截器

我们看下拦截器的实现

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);
		return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
	}
}

重点看intercept方法 当我们restTemplate执行请求操作时,就会被拦截器拦截进入intercept方法

而loadBalancer是LoadBalancerClient的具体实现 我们SC项目中使用 RibbonLoadBalancerClient类 具体方法实现

        @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);
	}

第一行:根据serviceId 获取对应的loadBalancer

第二行:根据loadBalancer获取具体的server(这里根据负载均衡规则,获取到具体的服务实例)

第三行:创建ribbonServer

第四行:执行具体请求


到处粗略的分析了 SC中@LoadBalanced使用负载均衡方式的restTemplate原理,后续有时间会进一步补充说明




    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值