文章目录
- 五、尾声
一、前言
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,这个就是本文的主角。
新建两个工程
好了,新建的时候注意两点:
(1)使用阿里镜像而不是spring官网;
(2)选择Spring Web/Web。
运行结果(使用restTemplate封装的http调用成功):
三、Ribbon负载均衡的使用
3.1 Ribbon负载均衡的原理
上面是一个order订单服务,一个user消费者,但是如果服务端(order)启动多个订单服务,客户端(user)需要使用Ribbon作负载均衡,Ribbon默认使用轮询的方式实现负载均衡。
Ribbon是客户端负载原理,如下图所示:
注意两点:
(1)Ribbon是客户端负载不是服务端负载;
(2)Ribbon有多中负载均衡方式,默认是轮询方式。
3.2 Ribbon负载均衡的实现
第一步,user工程(即客户端/使用端)导入ribbon依赖:
第二步,order工程(服务端/提供端)提供两种接口服务:
第三步,user工程(客户端/使用端)在application.properties文件中配置order服务的地址列表:
第四,user工程中主代码中加上负载均衡
对于RestTemplate,ioc容器中,一开始没有这个类实例,所有需要@Bean创建一个才能用@Autowired取出;
对于LoadBalancerClient,ioc容器中,一开始就有这个类实例,所有直接用@Autowired取出。
第五步,order工程中,打印一下客户端当前请求到的服务:
运行结果:
因为默认是轮询机制,所以是轮询了。
区别:Nginx负载均衡和Ribbon负载均衡的关系
回答:没有任何关系,Nginx是整个后端架构的网关层的负载均衡,Ribbon是微服务架构中(单体架构没有)服务调用方对于服务消费的负载均衡。
附加,也可以直接使用注解@LoadBalanced的方式
四、Ribbon源码解析
4.1 初始化三个bean,拦截所有带有@LoadBalance注解的RestTemplate
4.1.1 拦截器的引入
我们再来看一次Ribbon原理图,如果放在user工程中的ribbon组件,要起到负载均衡的作用,需要有一个拦截器,可以拦截restTemplate的请求,拦截之后,再解析出ip地址。
对于这个图的解释:当浏览器访问localhost:8088调用user工程的时候,user工程中ribbon组件可以对加了@LoadBalancer注解的所有restTemplate,就是拦截,使用的拦截器就是LoadBalancerIntercepter类,拦截之后再解析出ip地址,访问http://ip:port。
4.1.2 @Qualifier注解
拦截是怎样实现的,是如何识别所有加了@LoadBalancer注解的RestTemplate的,先来认识一下@Qualifier注解。
新建一个TestClass类
写一个测试Configuration类和一个测试Controller类:
这里注意,要修改一下SpringCloudUserServiceApplication类,让它扫描到Controller类注解:
运行结果:
小结:
所以,@Qualifier注解的作用就是:
(1)用于标注一些bean;(2)用于取出被@Qualifier标注的那些bean。
我们知道,在user工程中,restTemplate被@LoadBalanced注解修饰,如下:
让我们再来拿来看一下@LoadBalanced注解的定义,点进去,如下:
@LoadBalanced注解是一个被@Qualifier修饰的注解,所有RestTemplate这个bean也是被@Qualifier修饰的,那么springioc容器中,就可以通过查找被@Qualifier修饰的bean,精准的定位到restTemplate这个bean了,事实也确实是这样。
4.1.3 初始化三个bean,拦截所有带有@LoadBalance注解的RestTemplate
我们刚才上面的代码,使用过两种方式实现负载均衡,分别是直接注入一个LoadBalancerClient,和在RestTemplate上加上 @LoadBalanced
注解。
对于两种方式,底层走的代码路劲是不同的。
对于@LoadBalanced注解,这个注解被@Qualifier修饰,在LoadBalancerAutoConfiguration类中通过定位被@Qualifier修饰的类,来定位被@LoadBalanced修改的bean,放到一个名为restTemplates的list中。然后经过三个bean的处理,从前到后分别是:LoadBalancerInterceptor、RestTemplateCustomizer、SmartInitializingSingleton。
这三个bean的执行顺序是一定不会改变,因为第二个bean的定义需要springioc容器中存在第一个bean的实例,作为实参传递过来;第三个bean的定义需要springioc容器中存在第二个bean的实例,作为实参传递过来。
所以,对于@LoadBalanced注解,通过三个bean的定义,设置好了拦截器。
对于LoadBalancerClient,这个LoadBalancerClient的实现类是什么?答案是RibbonLoadBalancerClient,为什么?请看代码
附加:两种方式/两个配置类的联系
一图小结:两种方式/两种配置类
4.2 获得所有拦截住的LoadBalancer,再根据LoadBalancer获得到具体的Server,实现负载均衡
4.2.1 进入拦截器中
在上面我们知道,对于主代码中
restTemplate.getForObject("http://spring-cloud-order-service/orders", String.class);
一定会被拦截器拦截,进入到拦截器方法中,如下 LoadBalancerinterceptor.intercept()
进入到LoadBalancerClient.execute()
方法,这是一个接口,其具体实现是 RibbonLoadBalancerClient.execute()
,如下:
4.2.2 根据serviceId得到具体的负载均衡器
核心代码:IBalance=getLoadBalancer(serviceId)
现在我们来分析这一句 IBalance=getLoadBalancer(serviceId)
,根据serviceId得到负载均衡器,
这里注意:应该是进入到NameContextFactory的getInstance()方法,因为当前就是在SpringClientFactory类中。
这里,debug启动断点getInstance()这个地方,可以知道,最后得到的是一个ZoneAwareLoadBalancer对象,它是IBalancer接口的是一个实现类。
4.2.3 根据负载均衡器得到具体的服务器
核心代码:getServer(loadBalancer,hint)
回到RibbonLoadBalancerClient类的execute()方法,完成了getLoadBalancer(serviceId)
,现在是 getServer(loadBalancer, hint);
这个key就是负载均衡算法,默认是轮询。
继续点进去,如下:
附加:其实可以看看IRule接口的其他实现,即其他的负载均衡算法,如下:
比如,常见的随机算法:
4.2.4 生成RibbonServer并执行
到现在,执行完了getLoadBalancer()和getServer(),得到一个server,就生成RibbonServer,并执行,完成了。
最后小结一下这个部分(拦截器拦截+根据serviceId得到ZoneAwareLoadBalancer负载均衡器+根据负载均衡器balancer和负载均衡算法rule得到服务器节点server),如下图:
五、尾声
看顶级渣男如何邀约100个女朋友(一),完成了。
天天打码,天天进步!