spring-cloud-netflix-ribbon学习笔记(一)通过@LoadBalanced查询Ribbon是如何与SpringCloud整合的

久未见 提交于 2020-08-13 07:14:17

    首先用过Ribbon的都知道,要想在Spring-Cloud中使用Ribbon,需要在RestTemplate Bean上加入@LoadBalanced注解。那么我们可以先看看@LoadBalanced(代码如下)。

package org.springframework.cloud.client.loadbalancer;

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;

import org.springframework.beans.factory.annotation.Qualifier;

/**
 * Annotation to mark a RestTemplate or WebClient 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 {

}

    通过观察可以发现,@LoadBalanced注解在spring-cloud-commons工程下org.springframework.cloud.client.loadbalancer包中,并且@LoadBalanced的注解上也没有@Import类型导入Bean的注解。此时猜测是否有可能是某个地方判断了一个类上是有@LoadBalanced注解,如果有,就做一下处理,这是我们在idea中按ALT+F7进行查找(如下图)。发现在LoadBalancerWebClientBuilderBeanPostProcessor类中有相关代码。但是仔细观察,这里是判断某个bean是不是WebClient.Builder的实现,而且是在reactive包下,所以猜测是和reactive相关的。那么问题来了,这个@LoadBalanced注解到底是在哪里被使用了? 在这里插入图片描述
    此时笔者猜测是不是有LoadBalancedAutoConfiguration类型的类,结果一查,还真有,而且和@LoadBalanced在同一个包下(代码如下)。而且LoadBalancedAutoConfiguration被配置在当前工程的META-INF/spring.factories中,所以在SpringBoot启动的时候能被加载。

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {

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

	@Autowired(required = false)
	private List<LoadBalancerRequestTransformer> transformers = 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);
				}
			}
		});
	}

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

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

		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
				List<ClientHttpRequestInterceptor> list = new ArrayList<>(
						restTemplate.getInterceptors());
				list.add(loadBalancerInterceptor);
				restTemplate.setInterceptors(list);
			};
		}

	}

	/**
	 * Auto configuration for retry mechanism.
	 */
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(RetryTemplate.class)
	public static class RetryAutoConfiguration {

		@Bean
		@ConditionalOnMissingBean
		public LoadBalancedRetryFactory loadBalancedRetryFactory() {
			return new LoadBalancedRetryFactory() {
			};
		}

	}

	/**
	 * Auto configuration for retry intercepting mechanism.
	 */
	@Configuration(proxyBeanMethods = false)
	@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);
			};
		}

	}

}

    通过观察可以发现,在LoadBalancerAutoConfiguration有着

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

这么一个RestTemplate集合,他被标注了@LoadBalanced@Autowired(required = false)这两个注解,其中依赖注入的地方还不是必须的。这个地方就涉及到了Spring相关的知识了:因为@LoadBalanced@Qualifier注解标注,而@Qualifier是通过BeanName去依赖注入的,而且必须得找到一个Bean,不然Spring容器在启动的时候会报错,而不是给对象设置为NULL。所以这里的restTemplates 默认给了一个0个元素的集合。当我们自定义了RestTemplate并加上@Loadbalanced注解以后,Spring容器会自动帮我们吧定义好的RestTemplate加入到restTemplates集合中。

     接着我们在当前类中(LoadBalancerAutoConfiguration)发现了下面的代码

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

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

可以发现在loadBalancedRestTemplateInitializerDeprecated方法中,它是遍历了所有的restTemplateCustomizers(RestTemplate增强器),使用每个增强器对每个RestTemplate进行处理。那么RestTemplateCustomizer又是从哪里来,怎么处理的呢?接着在类中往下找,发现如下代码:

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

	@Bean
	@ConditionalOnMissingBean
	public RestTemplateCustomizer restTemplateCustomizer(
			final LoadBalancerInterceptor loadBalancerInterceptor) {
		return restTemplate -> {
			List<ClientHttpRequestInterceptor> list = new ArrayList<>(
					restTemplate.getInterceptors());
			list.add(loadBalancerInterceptor);
			restTemplate.setInterceptors(list);
		};
	}

}

可以发现在restTemplateCustomizer方法中定义了RestTemplateCustomizer 的处理逻辑,就是给每个RestTemplate添加了一个LoadBalancerInterceptor 的拦截器。而LoadBalancerInterceptor 是从上面的ribbonInterceptor方法中创建的。而新建LoadBalancerInterceptor的两个参数,LoadBalancerRequestFactory是从本类的loadBalancerRequestFactory中创建的。而创建LoadBalancerRequestFactoryLoadBalancerInterceptor LoadBalancerClient经过查找并没有在本类中。那么按照老规矩,因为LoadBalancerClient是一个接口,先看看他的实现类,在IDEA中按CART+H,可以发现它有两个实现类(如下图): 在这里插入图片描述
一个非阻塞的、一个阻塞的,那么我们就看一下非阻塞的RibbonLoadBalancerClient吧。注意它是在spring-cloud-netflix-ribbon工程下,和上面的@LoadBalanced、LoadBalancerAutoConfiguration不在同一个工程下哟。然后继续按ALT+F7查找一下RibbonLoadBalancerClient是在哪里被创建的。 在这里插入图片描述
可以发现RibbonLoadBalancerClient是被同包下的RibbonAutoConfiguration类中loadBalancerClient方法创建的。那么至此为止,Ribbon是如何与SpringCloud整合的就全部看明白了,下一篇继续来学习下LoadBalancerInterceptor是如何处理的吧~。来个流程图总结下目前的流程吧: 在这里插入图片描述

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!