> 文章列表 > Spring Cloud组件源码之LoadBalancer源码分析

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

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

" 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 {@Overridepublic 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 {@Overridepublic <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> {@Overridepublic 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自动注入的。

总结:

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

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