五、负载均衡服务调用Ribbon
文章目录
- 一、Ribbon 介绍
- 1.1 负载均衡策略介绍
- 1.2 Ribbon 简介
- 1.3 Ribbon 架构
- 二、Ribbon 的使用
- 三、Ribbon 核心组件 IRule
- 3.1 Ribbon 负载均衡规则介绍
- 3.2 更改默认的负载均衡规则
- 3.3 自定义负载均衡规则
一、Ribbon 介绍
1.1 负载均衡策略介绍
① 集中式LB
即在服务的消费方和提供方之间使用的独立的LB
设施(可以是硬件,如F5
,也可以是软件,如nginx
),由该设施负责把访问请求通过某种策略转发至服务的提供方;
② 进程内LB
将LB
逻辑继承到消费方,消费者从服务注册中心获知哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。Ribbon
就属于进程内LB
,它只是一个类库,集成与消费方进程,消费方通过它获取服务提供方的地址
1.2 Ribbon 简介
Spring Cloud Ribbon
是基于Netlix Ribbon
实现的一套客户端负载均衡的工具。简单的说,Ribbon
是Netflix
发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon
客户端组件提供一 系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer
(简称LB
)后面所有的机器,Ribbon
会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们很容易使用Ribbon
实现自定义的负载均衡算法。
不过Ribbon
已经进入了维护模式,NetFlix
给出的新的负载均衡方案:Spring Cloud Starter Loadbalancer
,但还并不完善。
1.3 Ribbon 架构
Ribbon
在工作时分为两步:
- 选择
EurekaServer
,它优先选择在同一个区域内负载较少的Server
- 根据用户指定的策略,在从
Server
取到的服务注册列表中选择一个地址
其中Ribbon
提供多种策略:比如轮询、随机、和根据响应时间加权。
Ribbon
其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和Eureka
结合只是其中的一个实例。
二、Ribbon 的使用
eureka-client
组件中本身封装了ribbon
组件,无需再添加
注入RestTemplate
组件:
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
使用RestTemplate
调用远程服务:
@Slf4j
@RequestMapping("/order")
@RestController
public class OrderController {
public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/{id}")
public CommonResult get(@PathVariable Long id){
log.info("-----远程调用支付服务中-----");
return restTemplate.getForObject(uri+"/payment/"+id,CommonResult.class);
}
}
xxxForObject()
和xxxForEntity()
的区别:
xxxForObject()
返回对象为响应体中数据转化成的对象,基本上可以理解为Json
xxxForEntity()
返回对象为ResponseEntity
对象, 包含了响应中的一 些重要信息,比如响应头、响应状态码、响应体等
注意:使用Rebbon
时,服务提供端的接口参数为实体类时,需要加上注解@RequestBody
,因为RestTemplate
调用传来的参数为json
格式,不加上强转不了。
三、Ribbon 核心组件 IRule
3.1 Ribbon 负载均衡规则介绍
Ribbon负载均衡算法 | 功能 |
---|---|
com.netflix.loadbalancer.RoundRobinRule | 轮询,默认 |
com.netflix.loadbalancer.RandomRule | 随机 |
com.netflix.loadbalancer.RetryRule | 先按照RoundRobinRule的策略获取服务,如果获取服务失败则再指定时间内重试,获取可用服务 |
WeightResponseTimeRule | 对RoundRobinRule的扩展,响应速度越快的实例选择权重越大 |
BestAvailableRule | 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务 |
AvailabilityFilterRule | 先过滤掉故障实例,再选择并发较小的实例 |
ZoneAvoidanceRule | 默认规则,复合判断server所在区域的性能和server的可用性选择服务器 |
3.2 更改默认的负载均衡规则
@Configuration
public class MySelfRule {
@Bean
public IRule myRule(){
return new RandomRule();
}
}
主启动类添加注解:
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class)
注意:这个自定义配置类不能放在@ComponentScan
所扫描的当前包下以及子包下。否则我们自定义的这个配置类就会被所有的Ribbon
客户端所共享,达不到特殊化定制的目的了,所以这个类最好别放到主启动类下。
3.3 自定义负载均衡规则
① 注入RestTemplate
代码中删除@LoadBalanced
,这样就不会使用默认的算法了。
② 创建LoadBalancer
接口
public interface LoadBalancer {
/** * * 从多个服务实例中选择一个服务实例 */
ServiceInstance instance(List<ServiceInstance> instances);
}
③ 实现该接口
实现原理:[ rest
接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标 ],每次服务 重启动后rest
接口计数从1开始。
@Slf4j
@Component
public class MyLoadBalance implements LoadBalancer {
private AtomicInteger visitCount = new AtomicInteger(0);
@Override
public ServiceInstance instance(List<ServiceInstance> instances) {
int index = getVisitCount()%instances.size();
return instances.get(index);
}
public final int getVisitCount() {
int current = 0;
int next = 0;
do {
current = this.visitCount.get();
next = current >= 2147483647 ? 0 : current + 1;
} while (!this.visitCount.compareAndSet(current, next));
log.info("-----第" + next + "访问-----");
return next;
}
}
乐观锁CAS
的介绍:https://blog.csdn.net/qq\_35571554/article/details/82892806
④ 调用
@Slf4j
@RequestMapping("/order")
@RestController
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
LoadBalancer loadBalancer;
@GetMapping("/{id}")
public CommonResult get(@PathVariable Long id){
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
if (instances ==null &&instances.size()<0){
return new CommonResult(200,"",null);
}
ServiceInstance instance = loadBalancer.instance(instances);
URI uri = instance.getUri();
log.info("-----远程调用支付服务中-----");
return restTemplate.getForObject(uri+"/payment/"+id,CommonResult.class);
}
}
还没有评论,来说两句吧...