springCloud的LoadBalance源码分析


title: SpringCloud的LoadBalance源码分析

欢迎查看Eetal的第二十篇博客–SpringCloud的LoadBalance源码分析

相关核心类

LoadBalanced
LoadBalancerClient
LoadBalancerAutoConfiguration
LoadBalancerInterceptor
RibbonAutoConfiguration
RibbonClassesConditions
AllNestedConditions
AbstractNestedCondition
AbstractNestedCondition.MemberMatchOutcomes
AbstractNestedCondition.MemberConditions
SpringBootCondition
ConditionOutcome

LoadBalanced注解与功能

通过在创建的RestTemplate上加入LoadBalanced注解,则该RestTemplate会自动成为负载均衡的RestTeplate,请求的url的hostname会作为服务名称去解析
用法如下

	@LoadBalanced
	@Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
	
	public String requestTest(){
	String url = "http://8781-eurekaClient/getClient2Info";
	return restTemplate.getForObject(url, String.class);
		}	

等价于以下写法

@Autowired
	RestTemplate restTemplate;
	
	@Autowired
    private LoadBalancerClient client;
	
	@RequestMapping("/getProviderInfo")
	public String getProviderInfo() {
		ServiceInstance instance = client.choose("8780-eurekaClient");
        String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/getInfo";
        return restTemplate.getForObject(url, String.class);
	}

源码分析

1.LoadBalanced注解

@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {

}

这里注意,该注解被标记了@Qualifier注解,后续使用@Autowired注解注入时,如果注解的属性也被LoadBalanced注解,则只装载一样被LoadBalanced注解的符合autowired的对象
2.LoadBalnceAutoConfigure

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

	}
	......
}

@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
这个配置类依赖于加载RestTemplate类以及创建了LoadBalancerClient对象
其中会创建一个RestTemplateCustomizer提供给上方创建智能初始化的bean,该bean初始化过程会使用这个RestTemplateCustomizer,为每个loadBalanced注解的restTemplate加上一个负载均衡的拦截器
如果配置生效,根据前面对LoadBalanced注解了解,此处的集合成员restTemplates就是IOC容器中,被LoadBalanced注解了的RestTemplate
RestTemplate是我们手动的导入,接下来查找LoadBalancerClient对象,在RibbonAutoConfiguration中
拦截器

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

	private LoadBalancerClient loadBalancer;

	private LoadBalancerRequestFactory 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,
				this.requestFactory.createRequest(request, body, execution));
	}

}

Ribbon自动配置

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

	/**
	 * {@link AllNestedConditions} that checks that either multiple classes are present.
	 */
	static class RibbonClassesConditions extends AllNestedConditions {

		RibbonClassesConditions() {
			super(ConfigurationPhase.PARSE_CONFIGURATION);
		}

		@ConditionalOnClass(IClient.class)
		static class IClientPresent {

		}

		@ConditionalOnClass(RestTemplate.class)
		static class RestTemplatePresent {

		}

		@ConditionalOnClass(AsyncRestTemplate.class)
		static class AsyncRestTemplatePresent {

		}

		@ConditionalOnClass(Ribbon.class)
		static class RibbonPresent {

		}

	}
	
	......
}

当IOC容器没有LoadBalancerClient实例时,RibbonAutoConfiguration会自动创建一个Ribbon实现的负载均衡客户端,是其子类
这也是为什么springCloud的Eureka客户端会默认使用Ribbon作为负载均衡
因为spring-cloud-starter-netflix-eureka-client工程引入依赖了spring-cloud-starter-netflix-ribbon

@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)的意思是该要求满足该类中所有condition相关注解(程序加载了对应的四个类)
public abstract class AllNestedConditions extends AbstractNestedCondition
public abstract class AbstractNestedCondition extends SpringBootCondition implements ConfigurationCondition

public abstract class SpringBootCondition implements Condition {

	private final Log logger = LogFactory.getLog(getClass());

	@Override
	public final boolean matches(ConditionContext context,
			AnnotatedTypeMetadata metadata) {
			//成立判断方法
		String classOrMethodName = getClassOrMethodName(metadata);
		try {
			ConditionOutcome outcome = getMatchOutcome(context, metadata);//调用getMatchOutcome
			logOutcome(classOrMethodName, outcome);
			recordEvaluation(context, classOrMethodName, outcome);
			return outcome.isMatch();//注意这里,outcome的match属性作为结果
		}
		catch (NoClassDefFoundError ex) {
			throw new IllegalStateException(
					"Could not evaluate condition on " + classOrMethodName + " due to "
							+ ex.getMessage() + " not "
							+ "found. Make sure your own configuration does not rely on "
							+ "that class. This can also happen if you are "
							+ "@ComponentScanning a springframework package (e.g. if you "
							+ "put a @ComponentScan in the default package by mistake)",
					ex);
		}
		catch (RuntimeException ex) {
			throw new IllegalStateException(
					"Error processing condition on " + getName(metadata), ex);
		}
	}
	......
}

public abstract class AbstractNestedCondition extends SpringBootCondition
		implements ConfigurationCondition {


	@Override
	public ConditionOutcome getMatchOutcome(ConditionContext context,
			AnnotatedTypeMetadata metadata) {
		String className = getClass().getName();
		MemberConditions memberConditions = new MemberConditions(context, className);
		MemberMatchOutcomes memberOutcomes = new MemberMatchOutcomes(memberConditions);
		return getFinalMatchOutcome(memberOutcomes);
	}
	
	protected static class MemberMatchOutcomes {

		private final List<ConditionOutcome> all;

		private final List<ConditionOutcome> matches;

		private final List<ConditionOutcome> nonMatches;

		public MemberMatchOutcomes(MemberConditions memberConditions) {
			this.all = Collections.unmodifiableList(memberConditions.getMatchOutcomes());
			List<ConditionOutcome> matches = new ArrayList<>();
			List<ConditionOutcome> nonMatches = new ArrayList<>();
			for (ConditionOutcome outcome : this.all) {
				//是否满足加到不同属性集合
				(outcome.isMatch() ? matches : nonMatches).add(outcome);
			}
			this.matches = Collections.unmodifiableList(matches);
			this.nonMatches = Collections.unmodifiableList(nonMatches);
		}
		......

	}	
}
public abstract class AllNestedConditions extends AbstractNestedCondition {


	@Override
	protected ConditionOutcome getFinalMatchOutcome(MemberMatchOutcomes memberOutcomes) {
		boolean match = hasSameSize(memberOutcomes.getMatches(), memberOutcomes.getAll());
		//match就是判断结果
		List<ConditionMessage> messages = new ArrayList<>();
		messages.add(ConditionMessage.forCondition("AllNestedConditions")
				.because(memberOutcomes.getMatches().size() + " matched "
						+ memberOutcomes.getNonMatches().size() + " did not"));
		for (ConditionOutcome outcome : memberOutcomes.getAll()) {
			messages.add(outcome.getConditionMessage());
		}
		return new ConditionOutcome(match, ConditionMessage.of(messages));
	}

	private boolean hasSameSize(List<?> list1, List<?> list2) {
		return list1.size() == list2.size();
	}
	......
}

ribbon的loadBalanceClient实现类

public class RibbonLoadBalancerClient implements LoadBalancerClient {

	private SpringClientFactory clientFactory;

	public RibbonLoadBalancerClient(SpringClientFactory clientFactory) {
		this.clientFactory = clientFactory;
	}

	@Override
	public URI reconstructURI(ServiceInstance instance, URI original) {
		Assert.notNull(instance, "instance can not be null");
		String serviceId = instance.getServiceId();
		RibbonLoadBalancerContext context = this.clientFactory
				.getLoadBalancerContext(serviceId);

		URI uri;
		Server server;
		if (instance instanceof RibbonServer) {
			RibbonServer ribbonServer = (RibbonServer) instance;
			server = ribbonServer.getServer();
			uri = updateToSecureConnectionIfNeeded(original, ribbonServer);
		}
		else {
			server = new Server(instance.getScheme(), instance.getHost(),
					instance.getPort());
			IClientConfig clientConfig = clientFactory.getClientConfig(serviceId);
			ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
			uri = updateToSecureConnectionIfNeeded(original, clientConfig,
					serverIntrospector, server);
		}
		return context.reconstructURIWithServer(server, uri);
	}

	@Override
	public ServiceInstance choose(String serviceId) {
		return choose(serviceId, null);
	}

	/**
	 * New: Select a server using a 'key'.
	 * @param serviceId of the service to choose an instance for
	 * @param hint to specify the service instance
	 * @return the selected {@link ServiceInstance}
	 */
	public ServiceInstance choose(String serviceId, Object hint) {
		Server server = getServer(getLoadBalancer(serviceId), hint);
		if (server == null) {
			return null;
		}
		return new RibbonServer(serviceId, server, isSecure(server, serviceId),
				serverIntrospector(serviceId).getMetadata(server));
	}

	@Override
	public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
			throws IOException {
		return execute(serviceId, request, null);
	}

	/**
	 * New: Execute a request by selecting server using a 'key'. The hint will have to be
	 * the last parameter to not mess with the `execute(serviceId, ServiceInstance,
	 * request)` method. This somewhat breaks the fluent coding style when using a lambda
	 * to define the LoadBalancerRequest.
	 * @param <T> returned request execution result type
	 * @param serviceId id of the service to execute the request to
	 * @param request to be executed
	 * @param hint used to choose appropriate {@link Server} instance
	 * @return request execution result
	 * @throws IOException executing the request may result in an {@link IOException}
	 */
	public <T> T execute(String serviceId, LoadBalancerRequest<T> 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);
	}

	@Override
	public <T> T execute(String serviceId, ServiceInstance serviceInstance,
			LoadBalancerRequest<T> request) throws IOException {
		Server server = null;
		if (serviceInstance instanceof RibbonServer) {
			server = ((RibbonServer) serviceInstance).getServer();
		}
		if (server == null) {
			throw new IllegalStateException("No instances available for " + serviceId);
		}

		RibbonLoadBalancerContext context = this.clientFactory
				.getLoadBalancerContext(serviceId);
		RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);

		try {
			T returnVal = request.apply(serviceInstance);
			statsRecorder.recordStats(returnVal);
			return returnVal;
		}
		// catch IOException and rethrow so RestTemplate behaves correctly
		catch (IOException ex) {
			statsRecorder.recordStats(ex);
			throw ex;
		}
		catch (Exception ex) {
			statsRecorder.recordStats(ex);
			ReflectionUtils.rethrowRuntimeException(ex);
		}
		return null;
	}
	......
}

请移步

个人主页: yangyitao.top

发布了35 篇原创文章 · 获赞 12 · 访问量 5032

猜你喜欢

转载自blog.csdn.net/weixin_44627989/article/details/91030785