Spring Cloud组件源码之LoadBalancer源码分析

2023-05-16

" Spring 到底是春天的来临万物复苏,还是春转夏的干燥又炎热呢?"  Spring的来临让JavaEE走向了另一个高度。便捷的开发,完美的生态。物极必反,学习Spring的成本越来越低,导致Java程序员越来越密集,越来越廉价…… 那么又有多少 Springer 耐下性子去研究Spring生态的架构和底层实现的细节呢??

版本信息如下:

Spring:5.3.23
Spring Boot:2.6.13
Spring Cloud:3.1.5
Spring Cloud LoadBalancer:3.1.5

正文:

由于原有的负载均衡组件Ribbon停止维护,而完美的Spring生态怎能允许缺少负载均衡组件呢?Spring Cloud官方自己造出了Spring Cloud LoadBalancer来代替原有的Ribbon。由于是官方自己写的组件,所以并没有像eureka、Feign那样抽出一个单独的组件包出来。放入到Spring Cloud Commons规范包中。

先会使用,再深入研究源码。由于太占用文章的排版空间,所以另起了一篇文章。对于整体流程来说不会自定义负载均衡策略也不影响,只不过是锦上添花。

Spring Cloud LoadBalancer 自定义负载均衡策略icon-default.png?t=N2N8https://blog.csdn.net/qq_43799161/article/details/130125370知其然,再知其所以然,我们先需要明白一次服务远程调用的大致流程,再深入其中挖掘细节。

 为了更明白LoadBalancer在其中的作用,所以我们从OpenFeign的远程调用开始分析,不懂OpenFeign的读者也没关系,当作黑盒即可。

public class FeignBlockingLoadBalancerClient implements Client {
	
	@Override
	public Response execute(Request request, Request.Options options) throws IOException {
		final URI originalUri = URI.create(request.url());
		// 拿到调用方的服务名。
		String serviceId = originalUri.getHost();

		…………

		// 调用负载均衡组件,经过负载均衡策略后返回最终远程调用的服务器实体
		ServiceInstance instance = loadBalancerClient.choose(serviceId, lbRequest);

		…………

		/**
		 * 解析ServiceInstance服务器实体,最终Feign发送Http请求到调用方服务器。
		 * */
		String reconstructedUrl = loadBalancerClient.reconstructURI(instance, originalUri).toString();
		Request newRequest = buildRequest(request, reconstructedUrl);
		LoadBalancerProperties loadBalancerProperties = loadBalancerClientFactory.getProperties(serviceId);
		return executeWithLoadBalancerLifecycleProcessing(delegate, options, newRequest, lbRequest, lbResponse,
				supportedLifecycleProcessors, loadBalancerProperties.isUseRawStatusCodeInResponseData());
	}
}

可以很清楚的看到,在OpenFeign中调用了负载均衡组件loadBalancerClient.choose根据负载均衡策略选取最终调用方ServiceInstance(ServiceInstance是Commons包定义的规范接口,也即是服务的实体对象),所以接下来分析loadBalancerClient.choose方法即可。

public class BlockingLoadBalancerClient implements LoadBalancerClient {

	@Override
	public <T> ServiceInstance choose(String serviceId, Request<T> request) {
		// 拿到具体的负载均衡策略对象
		ReactiveLoadBalancer<ServiceInstance> loadBalancer = loadBalancerClientFactory.getInstance(serviceId);
		if (loadBalancer == null) {
			return null;
		}
		// 发送请求到注册中心拿到注册表,然后根据负载均衡策略拿到最终的调用方。
		Response<ServiceInstance> loadBalancerResponse = Mono.from(loadBalancer.choose(request)).block();
		if (loadBalancerResponse == null) {
			return null;
		}
		// 方法调用方ServiceInstance实体。
		return loadBalancerResponse.getServer();
	}

}

这里拿到具体的负载均衡策略对象,然后发送请求到注册中心拿到注册表,最终根据负载均衡策略得到具体的调用方。

接下来,我们分析如何拿到负载均衡对象就闭环了,所以看到loadBalancerClientFactory.getInstance方法。

public class LoadBalancerClientFactory extends NamedContextFactory<LoadBalancerClientSpecification> implements ReactiveLoadBalancer.Factory<ServiceInstance> {
	
	@Override
	public ReactiveLoadBalancer<ServiceInstance> getInstance(String serviceId) {
		// ReactorServiceInstanceLoadBalancer这个接口是不是特别熟悉,没错就是负载均衡策略的抽象接口
		return getInstance(serviceId, ReactorServiceInstanceLoadBalancer.class);
	}

	public <T> T getInstance(String name, Class<T> type) {
		// 拿到Spring的上下文对象
		AnnotationConfigApplicationContext context = getContext(name);
		try {
			// 从Spring的工厂中拿到对象返回。
			return context.getBean(type);
		}
		catch (NoSuchBeanDefinitionException e) {
			// ignore
		}
		return null;
	}

}

可以看到实现非常简单,就是从Spring工厂中拿到ReactorServiceInstanceLoadBalancer类型的对象,而ReactorServiceInstanceLoadBalancer类型就是负载均衡策略。不管是我们自定义负载均衡对象还是Spring Cloud LoadBalancer默认实现RoundRobinLoadBalancer轮训都会通过@LoadBalancerClient(就是一个@Configuration)+ @Bean 注入给Spring,而这里从Spring工厂中取出来然后回掉choose方法根据策略得到最终的调用方。这不就闭环啦~!

顺带提一下上篇文章中有提到过Spring Cloud LoadBalancer帮开发实现了RoundRobinLoadBalancer轮询和RandomLoadBalancer随机,那么顺便找一下哪里注入到Spring,不用想,开发者不需要手动注入,那肯定是Spring boot自动注入的。

总结:

先从主体入手,再慢慢深入,这对于看源码是一种很不错的学习方式~!

最后,如果本帖对您有一定的帮助,希望能点赞+关注+收藏!您的支持是给我最大的动力,后续会一直更新各种框架的使用和框架的源码解读~!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring Cloud组件源码之LoadBalancer源码分析 的相关文章

随机推荐