章
目
录
一、环境准备
为了实现Ribbon负载均衡的演示,我们首先将项目恢复到我们第12小节的状态,即:我们有
- 1个服务消费者:
cloud-consumer-order80
- 2个服务提供者:
cloud-provider-payment8001
和cloud-provider-payment8002
- 3个服务注册中心:
cloud-eureka-server7001
、cloud-eureka-server7002
、cloud-eureka-server7003
二、架构说明
Ribbon在工作时分成两步:
1)第一步先选择Eureka Server ,它优先选择在同一个区域内负载较少的server。
2)第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址 。
其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权。
总结: Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。
三、Ribbon配置
我们在之前的项目中,就已经可以实现负载均衡了,但是我们在pom里确并没有发现导入Ribbon的相关依赖,这是为什么?原因就在于在我们导入的Eureka Client依赖
中已经包含了Ribbon相关依赖,如图:
如果是没有整合Ribbon依赖的需要单独引入Ribbon,其GAV坐标为:
<dependency> <groupId>com.netflix.ribbon</groupId> <artifactId>ribbon</artifactId> <version>2.2.2</version> </dependency>
四、RestTemplate
我们之前在Order80
的OrderController
中都是使用的RestTemplate
的getForObject/postForObject
方法,现在我们再新增两个方法,使用下getForEntity/postForEntity
方法。
@GetMapping("/consumer/payment/getForEntity/{id}") public CommonResult<Payment> getPayment2(@PathVariable("id") Long id){ ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL+"/payment/get/"+id,CommonResult.class); log.info(entity.getStatusCode()+"\t"+entity.getHeaders()); if(entity.getStatusCode().is2xxSuccessful()){ return entity.getBody(); }else{ return new CommonResult(444,"查询失败"); } } @GetMapping("/consumer/payment/postForEntity") public CommonResult<Payment> create2(Payment payment){ ResponseEntity<CommonResult> entity = restTemplate.postForEntity(PAYMENT_URL+"/payment/create",payment,CommonResult.class); if(entity.getStatusCode().is2xxSuccessful()){ return entity.getBody(); }else{ return new CommonResult(444,"新增失败"); } }
getForObject
和postForObject
返回对象为响应体中数据转化成的对象,基本上可以理解为JsongetForEntity
和postForEntity
返回对象为ResponseEntity对象, 包含了响应中的- -些重要信息,比如响应头、响应状态码、响应体等接下来测试get和create发现正常使用,在此不再赘述
五、Ribbon负载均衡策略
Ribbon作为后端负载均衡器,比Nginx更注重的是承担并发而不是请求分发,可以直接感知后台动态变化来指定分发策略。它一共提供了7种负载均衡策略,均是IRule
接口的实现类,IRule
的继承实现图如下:
其提供的7中负载均衡实现类及说明如下:
六、如何替换Ribbon负载均衡策略
这里我们以将原来默认的轮询策略替换为随机策略为例:
第1步:新建自定义规则包
修改cloud-consumer-order80
代码,新建com.panziye.myrule
包
这个自定义配置类不能放在
@ComponentScan
所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制的目的了。而我们的主启动类上的
@SpringBootApplication
注解源码上就带有@ComponentScan
注解,因此我们自定义的规则包,不能与主启动类在同一个包下。第2步:自定义规则类
我们在myrule包下新建名为MyRibbonRule
规则类(这里我们使用注解配置的方式实现):
package com.panziye.myrule; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyRibbonRule { @Bean public IRule MyRibbonRule(){ // 使用随机策略 return new RandomRule(); } }
第3步:修改主启动类
我们需要在主启动类OrderMain80
上新增@RibbonClient
注解:
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MyRibbonRule.class)
第4步:测试
启动着5个模块(注意先后顺序),测试查看访问策略确实变成了随机,在此不再演示。
七、Ribbon轮询策略原理
1)Ribbon轮询策略原理:
rest接口第几次请求数%服务器集群总数量=实际调用服务器位置下标,每次服务 重启动后rest接口计数从1开始
2)源码
我们可以通过ctrl+n
搜索IRule
,查看IRule
相关源码,再ctrl+h
查看其实现类,我们这里查看RoundRobinRule
,这里面涉及到自旋锁、CAS算法等知识(JUC),有兴趣的可自行探究。
3)手写负载均衡算法,有兴趣可自行学习