章
目
录
一、环境准备
为了实现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)手写负载均衡算法,有兴趣可自行学习







