文章目录



一、前言

ribbon是一个顶级渣男,也是一个顶级时间管理大师,他有100个女朋友,但是这100个女朋友相互之间并不知道,因为ribbon每次都邀请一个女朋友约会,坚决不要各个女朋友认识。

ribbon手里有各个女朋友的电话号码、微信号、家庭住址和家庭成员信息,永远不会搞错,ribbon每晚上约会一个女朋友,他是怎样选择的呢?他有各种方案,可以给女朋友编号,从1号到100号,这样的话,连续三个月都不换重的,爽乖乖;可以每天晚上翻牌子,随便抽取一个,好有古代皇帝的感觉;也可以尽量邀请那些约会不要让他等他久女孩子,毕竟渣男是时间宝贵。

本文,手把手教你ribbon如何轻松应对100个女朋友的约会的情场秘籍。

二、远程调用的基础,RestTemplate封装Http请求

Spring Cloud分为两个部分,分别是Spring Cloud Netflix和Spring Cloud Alibaba。其中,

Spring Cloud Netflix: Ribbon、Hystrix、Feign、Eureka等;

Spring Cloud Alibaba:dubbo、nacos注册中心、seata、rocketMQ消息队列、sentinel等。

单体架构中,整个项目部署到Linux就是一个war包,但是由于业务量的增加,整个软件系统部署到一个服务器,硬件资源撑不起这么大的业务访问量,所以引入了微服务架构。

对于微服务架构,将整个软件系统拆分为不同的服务,每一个服务部署到Linux上就一个war包,服务与服务之间的通信就是微服务通信。

所以,正是因为业务量的增加引入了微服务架构,因为引入了微服务架构不可避免地需要实现各个服务之间的通信。

通信如何实现,主要有两种方式:

(1)使用专业的RPC框架:如Dubbo等;

(2)自己手写RPC实现微服务之间的通信。

实现了微服务通信后,就要面临这个用户请求的负债均衡,这个就需要用到Spring Cloud Netflix Ribbon,这个就是本文的主角。

新建两个工程

看顶级渣男如何邀约100个女朋友(一)_spring

看顶级渣男如何邀约100个女朋友(一)_负载均衡_02


好了,新建的时候注意两点:
(1)使用阿里镜像而不是spring官网;
(2)选择Spring Web/Web。


看顶级渣男如何邀约100个女朋友(一)_负载均衡_03

看顶级渣男如何邀约100个女朋友(一)_拦截器_04

运行结果(使用restTemplate封装的http调用成功):

看顶级渣男如何邀约100个女朋友(一)_负载均衡_05

三、Ribbon负载均衡的使用

3.1 Ribbon负载均衡的原理

上面是一个order订单服务,一个user消费者,但是如果服务端(order)启动多个订单服务,客户端(user)需要使用Ribbon作负载均衡,Ribbon默认使用轮询的方式实现负载均衡。

Ribbon是客户端负载原理,如下图所示:

看顶级渣男如何邀约100个女朋友(一)_拦截器_06


注意两点:
(1)Ribbon是客户端负载不是服务端负载;
(2)Ribbon有多中负载均衡方式,默认是轮询方式。


3.2 Ribbon负载均衡的实现

第一步,user工程(即客户端/使用端)导入ribbon依赖:

看顶级渣男如何邀约100个女朋友(一)_spring_07

第二步,order工程(服务端/提供端)提供两种接口服务:

看顶级渣男如何邀约100个女朋友(一)_负载均衡_08

第三步,user工程(客户端/使用端)在application.properties文件中配置order服务的地址列表:

看顶级渣男如何邀约100个女朋友(一)_负载均衡_09

第四,user工程中主代码中加上负载均衡

看顶级渣男如何邀约100个女朋友(一)_spring_10


对于RestTemplate,ioc容器中,一开始没有这个类实例,所有需要@Bean创建一个才能用@Autowired取出;
对于LoadBalancerClient,ioc容器中,一开始就有这个类实例,所有直接用@Autowired取出。


第五步,order工程中,打印一下客户端当前请求到的服务:

看顶级渣男如何邀约100个女朋友(一)_负载均衡_11

运行结果:

看顶级渣男如何邀约100个女朋友(一)_拦截器_12

看顶级渣男如何邀约100个女朋友(一)_负载均衡_13

因为默认是轮询机制,所以是轮询了。


区别:Nginx负载均衡和Ribbon负载均衡的关系
回答:没有任何关系,Nginx是整个后端架构的网关层的负载均衡,Ribbon是微服务架构中(单体架构没有)服务调用方对于服务消费的负载均衡。


附加,也可以直接使用注解@LoadBalanced的方式

看顶级渣男如何邀约100个女朋友(一)_负载均衡_14

四、Ribbon源码解析

4.1 初始化三个bean,拦截所有带有@LoadBalance注解的RestTemplate

4.1.1 拦截器的引入

我们再来看一次Ribbon原理图,如果放在user工程中的ribbon组件,要起到负载均衡的作用,需要有一个拦截器,可以拦截restTemplate的请求,拦截之后,再解析出ip地址。

看顶级渣男如何邀约100个女朋友(一)_拦截器_15

对于这个图的解释:当浏览器访问localhost:8088调用user工程的时候,user工程中ribbon组件可以对加了@LoadBalancer注解的所有restTemplate,就是拦截,使用的拦截器就是LoadBalancerIntercepter类,拦截之后再解析出ip地址,访问http://ip:port。

4.1.2 @Qualifier注解

拦截是怎样实现的,是如何识别所有加了@LoadBalancer注解的RestTemplate的,先来认识一下@Qualifier注解。

新建一个TestClass类

看顶级渣男如何邀约100个女朋友(一)_spring_16

写一个测试Configuration类和一个测试Controller类:

看顶级渣男如何邀约100个女朋友(一)_spring_17

看顶级渣男如何邀约100个女朋友(一)_拦截器_18

这里注意,要修改一下SpringCloudUserServiceApplication类,让它扫描到Controller类注解:

看顶级渣男如何邀约100个女朋友(一)_负载均衡_19

运行结果:

看顶级渣男如何邀约100个女朋友(一)_拦截器_20

小结:

所以,@Qualifier注解的作用就是:

(1)用于标注一些bean;(2)用于取出被@Qualifier标注的那些bean。

我们知道,在user工程中,restTemplate被@LoadBalanced注解修饰,如下:

看顶级渣男如何邀约100个女朋友(一)_spring_21

让我们再来拿来看一下@LoadBalanced注解的定义,点进去,如下:

看顶级渣男如何邀约100个女朋友(一)_负载均衡_22

@LoadBalanced注解是一个被@Qualifier修饰的注解,所有RestTemplate这个bean也是被@Qualifier修饰的,那么springioc容器中,就可以通过查找被@Qualifier修饰的bean,精准的定位到restTemplate这个bean了,事实也确实是这样。

看顶级渣男如何邀约100个女朋友(一)_spring_23

4.1.3 初始化三个bean,拦截所有带有@LoadBalance注解的RestTemplate

我们刚才上面的代码,使用过两种方式实现负载均衡,分别是直接注入一个LoadBalancerClient,和在RestTemplate上加上 ​​@LoadBalanced​​ 注解。

看顶级渣男如何邀约100个女朋友(一)_spring_24

对于两种方式,底层走的代码路劲是不同的。

对于@LoadBalanced注解,这个注解被@Qualifier修饰,在LoadBalancerAutoConfiguration类中通过定位被@Qualifier修饰的类,来定位被@LoadBalanced修改的bean,放到一个名为restTemplates的list中。然后经过三个bean的处理,从前到后分别是:LoadBalancerInterceptor、RestTemplateCustomizer、SmartInitializingSingleton。

看顶级渣男如何邀约100个女朋友(一)_spring_25

看顶级渣男如何邀约100个女朋友(一)_负载均衡_26

这三个bean的执行顺序是一定不会改变,因为第二个bean的定义需要springioc容器中存在第一个bean的实例,作为实参传递过来;第三个bean的定义需要springioc容器中存在第二个bean的实例,作为实参传递过来。

所以,对于@LoadBalanced注解,通过三个bean的定义,设置好了拦截器。

对于LoadBalancerClient,这个LoadBalancerClient的实现类是什么?答案是RibbonLoadBalancerClient,为什么?请看代码

看顶级渣男如何邀约100个女朋友(一)_负载均衡_27

附加:两种方式/两个配置类的联系

看顶级渣男如何邀约100个女朋友(一)_spring_28

一图小结:两种方式/两种配置类

看顶级渣男如何邀约100个女朋友(一)_spring_29

4.2 获得所有拦截住的LoadBalancer,再根据LoadBalancer获得到具体的Server,实现负载均衡

4.2.1 进入拦截器中

在上面我们知道,对于主代码中

restTemplate.getForObject("http://spring-cloud-order-service/orders", String.class);

一定会被拦截器拦截,进入到拦截器方法中,如下 ​​LoadBalancerinterceptor.intercept()​

看顶级渣男如何邀约100个女朋友(一)_拦截器_30

进入到​​LoadBalancerClient.execute()​​​方法,这是一个接口,其具体实现是 ​​RibbonLoadBalancerClient.execute()​​,如下:

看顶级渣男如何邀约100个女朋友(一)_拦截器_31

4.2.2 根据serviceId得到具体的负载均衡器

核心代码:​​IBalance=getLoadBalancer(serviceId)​

现在我们来分析这一句 ​​IBalance=getLoadBalancer(serviceId)​​,根据serviceId得到负载均衡器,

看顶级渣男如何邀约100个女朋友(一)_拦截器_32

看顶级渣男如何邀约100个女朋友(一)_spring_33

看顶级渣男如何邀约100个女朋友(一)_拦截器_34

看顶级渣男如何邀约100个女朋友(一)_负载均衡_35


这里注意:应该是进入到NameContextFactory的getInstance()方法,因为当前就是在SpringClientFactory类中。


看顶级渣男如何邀约100个女朋友(一)_spring_36

这里,debug启动断点getInstance()这个地方,可以知道,最后得到的是一个ZoneAwareLoadBalancer对象,它是IBalancer接口的是一个实现类。

看顶级渣男如何邀约100个女朋友(一)_负载均衡_37

4.2.3 根据负载均衡器得到具体的服务器

核心代码:​​getServer(loadBalancer,hint)​

回到RibbonLoadBalancerClient类的execute()方法,完成了​​getLoadBalancer(serviceId)​​​,现在是 ​​getServer(loadBalancer, hint);​

看顶级渣男如何邀约100个女朋友(一)_spring_38

看顶级渣男如何邀约100个女朋友(一)_负载均衡_39

看顶级渣男如何邀约100个女朋友(一)_拦截器_40

看顶级渣男如何邀约100个女朋友(一)_拦截器_41


这个key就是负载均衡算法,默认是轮询。


继续点进去,如下:

看顶级渣男如何邀约100个女朋友(一)_负载均衡_42

附加:其实可以看看IRule接口的其他实现,即其他的负载均衡算法,如下:

看顶级渣男如何邀约100个女朋友(一)_负载均衡_43

比如,常见的随机算法:

看顶级渣男如何邀约100个女朋友(一)_拦截器_44

4.2.4 生成RibbonServer并执行

到现在,执行完了getLoadBalancer()和getServer(),得到一个server,就生成RibbonServer,并执行,完成了。

看顶级渣男如何邀约100个女朋友(一)_负载均衡_45

最后小结一下这个部分(拦截器拦截+根据serviceId得到ZoneAwareLoadBalancer负载均衡器+根据负载均衡器balancer和负载均衡算法rule得到服务器节点server),如下图:

看顶级渣男如何邀约100个女朋友(一)_spring_46

五、尾声

看顶级渣男如何邀约100个女朋友(一),完成了。

天天打码,天天进步!