【云原生&微服务三】SpringCloud之Ribbon是这样实现负载均衡的(源码剖析@LoadBalanced原理)

网友投稿 1160 2022-09-30

【云原生&微服务三】SpringCloud之Ribbon是这样实现负载均衡的(源码剖析@LoadBalanced原理)

【云原生&微服务三】SpringCloud之Ribbon是这样实现负载均衡的(源码剖析@LoadBalanced原理)

文章目录

​​一、前言​​​​二、@LoadBalanced注解原理​​

​​1、找找@LoadBalanced注解在哪​​

​​1)@LoadBalanced流程图总述​​​​2)LoadBalancerAutoConfiguration自动装配类​​

​​1> RestTemplateCustomizer从哪里来的?​​​​2> LoadBalancerInterceptor-​​

​​2、RestTemplate执行请求时的入口​​

​​1)LoadBalancerClient何时注入到Spring容器​​​​2)LoadBalancerInterceptor#intercept()方法拦截请求​​

​​3、RibbonLoadBalancerClient执行请求​​

​​1)获取ILoadBalancer流程图总述​​​​2)如何获取到ILoadBalancer?​​

​​1> 为什么默认实例化的ILoadBalancer是ZoneAwareLoadBalancer?​​

​​三、后续文章​​

一、前言

在前面的文章,博主聊了Ribbon如何与SpringCloud、Eureka集成,Ribbon如何自定义负载均衡策略:

​​【云原生&微服务一】SpringCloud之Ribbon实现负载均衡详细案例(集成Eureka、Ribbon)​​​​【云原生&微服务二】SpringCloud之Ribbon自定义负载均衡策略(含Ribbon核心API)​​

前面我们学会了怎么使用Ribbon,那么为什么给RestTemplate类上加上了@LoadBalanced注解就可以使用Ribbon的负载均衡?SpringCloud是如何集成Ribbon的?Ribbon如何作用到RestTemplate上的?如何获取到的ILoadBalancer?

本文就这几个问题展开讨论。

PS: 文章中涉及到的SpringBoot相关知识点,比如自动装配,移步博主的SpringBoot专栏:​​Spring Boot系列​​。

PS2:Ribbon依赖Spring Cloud版本信息如下:

org.springframework.boot spring-boot-dependencies 2.3.7.RELEASE pom import org.springframework.cloud spring-cloud-dependencies Hoxton.SR8 pom import com.alibaba.cloud spring-cloud-alibaba-dependencies 2.2.5.RELEASE pom import

下面以请求​​或 配置类XXXConfiguration。

上面说了@LoadBalanced属于​​spring-cloud-commons项目​​,找到其jar包下/META-INF/spring.factories文件(为啥找这个文件呢,见​​SpringBoot自动装配机制原理​​):

既然是负载均衡,我们从类的命名来推测,锁定​​AsyncLoadBalancerAutoConfiguration​​​类和​​LoadBalancerAutoConfiguration​​​类;这两个类选哪个呢,“小公鸡点到谁就是谁”?还是看命名,​​AsyncLoadBalancerAutoConfiguration​​​中有Async,是异步负载均衡请求的;我们看同步,同步好debug,进一步锁定到​​LoadBalancerAutoConfiguration​​类。

1)@LoadBalanced流程图总述

2)LoadBalancerAutoConfiguration自动装配类

进到类里,我们发现它组合了一个RestTemplate集合,即:我们创建的那个RestTemplate实例(​​被@LoadBalanced注解标注​​)会放到这里来!

此处细节涉及到SpringBoot的源码,为了避免偏题,本文仅提供一种思路,不做详细解释;

在LoadBalancerAutoConfiguration类中会注入一个SmartInitializingSingleton实例;

SmartInitializingSingleton接口中只有一个​​afterSingletonsInstantiated()​​方法;在SpringBoot启动过程中,当RestTemplate实例化完之后,会执行这个方法,做如下操作:

遍历每个RestTemplate实例,然后再遍历所有的​​RestTemplateCustomizer​​​对每个​​RestTemplate​​​实例做定制化操作,即添加-​​LoadBalancerInterceptor​​操作。

1> RestTemplateCustomizer从哪里来的?

还是在​​LoadBalancerAutoConfiguration​​​类中会通过@Bean方法注入​​RestTemplateCustomizer​​;

2> LoadBalancerInterceptor-

RestTemplateCustomizer所做的定制化就是给RestTemplate添加一个​​LoadBalancerInterceptor ​​​-;​​LoadBalancerInterceptor​​​和​​RestTemplateCustomizer​​都在LoadBalancerAutoConfiguration的静态内部类LoadBalancerInterceptorConfig中,在SpringBoot启动流程中我们知道,同一个类中@Bean方法的加载是从上至下的,所以肯定是LoadBalancerInterceptor先加载到Spring容器中。

此外,LoadBalancerAutoConfiguration类中还会自动装配一些Retry…相关的类,用于请求重试。

看到这,我们知道了Ribbon如何作用到RestTemplate上!但是好像还差一点东西,我要通过RestTemplate做一个操作时,入口在哪?

2、RestTemplate执行请求时的入口

上面我们说到,针对每一个RestTemplate,都会给其添加一个​​LoadBalancerInterceptor​​-,所以我们对RestTemplate执行某个操作时,会被LoadBalancerInterceptor所拦截。

LoadBalancerInterceptor中组合了​​LoadBalancerClient​​​,通过​​LoadBalancerClient​​做负载均衡;

然而想要将LoadBalancerInterceptor注入到Spring容器,需要先将​​LoadBalancerClient​​注入到Spring容器。那么LoadBalancerClient是何时注入的?

1)LoadBalancerClient何时注入到Spring容器

去哪找?我去哪找?既然我们集成了​​netflix-ribbon​​​,找一下以netflix-ribbon命名的jar包;找到​​spring-cloud-netflix-ribbon​​jar包;老规矩,SpringClout项目直接就开找自动装配类XxxAutoConfiguration,自动装配类找不到注入Bean,再找配置类XxxConfiguration。

找到​​RibbonAutoConfiguration​​​类,其中会注入​​LoadBalancerClient​​:

注意​​@AutoConfigureAfter​​​注解和​​@AutoConfigureBefore​​​注解,其表示:RibbonAutoConfiguration类加载要发生在​​LoadBalancerAutoConfiguration​​​类加载之前、发生在​​EurekaClientAutoConfiguration​​类加载之后。

到这里,​​LoadBalancerClient​​​实例比定在​​LoadBalancerAutoConfiguration​​​加载之前已经注入到了Spring容器,我们回到​​LoadBalancerAutoConfiguration​​​类,看其中注入​​LoadBalancerInterceptor​​类到Spring容器的地方;

2)LoadBalancerInterceptor#intercept()方法拦截请求

LoadBalancerInterceptor类实现​​ClientHttpRequestInterceptor​​​接口,其中只有一个核心方法:​​intercept()​​用于拦截通过RestTemplate执行的请求。

在intercept()方法中,会基于​​LoadBalancerRequestFactory​​创建出来一个对RestTemplate请求包装后的请求,并将请求转发给组合的LoadBalancerClient接口的实现类RibbonLoadBalancerClient#execute()方法去执行;

这里我们也就知道了真正执行RestTemplate请求方法的入口是​​RibbonLoadBalancerClient#execute()​​。

下面我们就继续来看​​RibbonLoadBalancerClient#execute()​​里面都做了什么?

3、RibbonLoadBalancerClient执行请求

从LoadBalancerInterceptor#intercept()方法进入到RibbonLoadBalancerClient#execute()方法代码执行流程如下:

最终进入到RibbonLoadBalancerClient#execute()方法中会做三件事:

根据服务名从Ribbon自己的​​Spring子上下文​​中获取服务名对应的ApplicationContext,进而获取到ILoadBalancer;根据负载均衡器​​ILoadBalancer​​​从Eureka Client获取到的​​List​​中选出一个Server。拼装真正的请求URI,做HTTP请求调用。

本文我们重点看一下如何获取到ILoadBalancer?

1)获取ILoadBalancer流程图总述

2)如何获取到ILoadBalancer?

进入到#​​RibbonLoadBalancerClient#getLoadBalancer(String serviceId)​​方法;

protected ILoadBalancer getLoadBalancer(String serviceId) { // 通过SpringClientFactory来获取对应的LoadBalancer return this.clientFactory.getLoadBalancer(serviceId);}

其将请求交给SpringClientFactory的getLoadBalancer(String)方法处理:

看SpringClientFactory的类图:

SpringClientFactory继承自NamedContextFactory,所以​​super.getInstance(name, type)​​方法为NamedContextFactory#getInstance()方法:

SpringClientFactory不是spring包下的,而是spring cloud与ribbon整合代码的包(org.springframework.cloud-flix.ribbon)下的;

默认可以通过父类NamedContextFactory的getLoadBalacner()方法获取到ILoadBalancer接口对应的实例​​ZoneAwareLoadBalancer​​​。即获取到的ILoadBalancer为​​ZoneAwareLoadBalancer​​。

1> 为什么默认实例化的ILoadBalancer是ZoneAwareLoadBalancer?

在​​spring-cloud-netflix-ribbon​​​jar包下找到​​RibbonClientConfiguration​​类,RibbonClientConfiguration类中加载了的ILoadBalancer的实例bean --> ZoneAwareLoadBalancer:

默认的LoadBalancer是ZoneAwareLoadBalancer,ZoneAwareLoadBalancer类图如下:

ZoneAwareLoadBalancer继承自DynamicServerListLoadBalancer,DynamicServerListLoadBalancer继承自BaseLoadBalancer。

三、后续文章

下一篇文章,我们继续分析:

ZoneAwareLoadBalancer(属于ribbon)如何与eureka整合,通过eureka client获取到对应注册表?ZoneAwareLoadBalancer如何持续从Eureka中获取最新的注册表信息?如何根据负载均衡器​​ILoadBalancer​​​从Eureka Client获取到的​​List​​中选出一个Server?

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Linux编程入门三多线程
下一篇:小程序怎么获取当前日期(微信小程序显示当前时间)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~