十一、服务调用-Ribbon(负载均衡调用)
1.概述
1.Ribbon是什么
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的,包括后续我们将要介绍的Feign,它也是基于Ribbon实现的工具。所以,对Spring Cloud Ribbon的理解和使用,对于我们使用Spring Cloud来构建微服务非常重要。
实现负载均衡的算法
2.官网
https://github.com/Netflix/ribbon/wiki/Getting-Started
3.能干嘛
LB(负载均衡):集中式LB、进程内LB
一句话概括:负载均衡+RestTemplate调用。
2.RestTemplate的使用
getForObject/postForObject方法:返回json格式
getForEntity/postEntity方法:返回Entity格式, 之外还带有更加详细的信息,如头信息等。
3.pom
spring-cloud-starter-netflix-eureka-client包中包含spring-cloud-starter-netflix-ribbon包, 故不需要额外引入。
4.Ribbon核心组件IRule
作用:根据特定算法从服务列表中选取一个要访问的服务。
1.IRule算法列表
- com.netflix.loadbalancer.RoundRobinRule
- 轮询
- com.netflix.loadbalancer.RandomRule
- 随机
- com.netflix.loadbalancer.RetryRule
- 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务
- WeightedResponseTimeRule
- 对RoundRobinRule的扩展,响应速度越快的实例选择权重越多大,越容易被选择
- BestAvailableRule
- 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
- AvailabilityFilteringRule
- 先过滤掉故障实例,再选择并发较小的实例
- ZoneAvoidanceRule
- 默认规则,复合判断server所在区域的性能和server的可用性选择服务器
2.替换默认IRule算法
1.在eureka-order80工程中新建规则定义包com.dt.myrule
注意,不能与主启动类在同一个包下!
2.创建规则配置类MySelfRule
package com.dt.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 MySelfRule {
@Bean
public IRule diyRule() {
// 随机算法
return new RandomRule();
}
}
3.主启动类添加@RibbonClient
package com.dt.springcloud;
import com.dt.myrule.MySelfRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
/**
* 主启动类
* 若项目不需要连接数据库,需要增加:@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
*/@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableEurekaClient
@EnableDiscoveryClient
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)// 作用是替换ribbon负载均衡规则
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class, args);
}
}
5.Ribbon负载均衡算法
1.原理
rest接口第几次请求次数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务器重启后,rest接口计数从1开始。
2.自定义本地负载均衡器。
1.支付8001和8002controller增加获得端口方法。
/**
* 获得服务及端口。
* @return
*/@GetMapping("/payment/lb")
public String getPaymentLB()
{
return serverPort;
}
2.订单80的ApplicationContextConfig 注释掉@LoadBalanced ,以便使用自己的Lb算法。
3.自定义LoadBalancer接口
package com.dt.springcloud.loadbalanced;
import org.springframework.cloud.client.ServiceInstance;
import java.util.List;
/**
* LoadBalancer 接口
*/
public interface LoadBalancer {
/**
* 收集服务器总共有多少台能够提供服务的机器,并放到list里面
* <p>
* 因为有List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances(PAYMENT_SERVICE_NAME); * * @param serviceInstances:
*/ ServiceInstance instances(List<ServiceInstance> serviceInstances);
}
4.LoadBanlancer实现类
package com.dt.springcloud.loadbalanced;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* LoadBalancer实现类
* */
@Component
@Slf4j
public class MyLoadBalancer implements LoadBalancer {
private final AtomicInteger atomicInteger = new AtomicInteger(0);
/**
* 获取下一次要调用的服务实例 下标
*/
@Override
public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
int index = this.getAndIncrement() % serviceInstances.size();
return serviceInstances.get(index);
}
/**
* 自写负载均衡算法: rest接口第几次请求数% 服务集群实例总数量 = 实际调用服务实例下标 ,每次重启后rest接口计数从1开始
*/
private int getAndIncrement() {
int current;
int next;
// atomicInteger.compareAndSet 自旋锁
do {
current = this.atomicInteger.get();
// 2147483647 就是Integer.MAX_VALUE
next = current >= 2147483647 ? 0 : current + 1;
} while (!this.atomicInteger.compareAndSet(current,next));
log.info("====>*******第几次访问,次数next:{}",next);
return next;
}
}
5.业务类改造,新增以下内容
/**
* 注入自定义负载均衡对象
*/
@Autowired
private LoadBalancer myLoadBalancer;
/**
* 注入RestTemplate对象
*/
@Autowired
// @Qualifier("diyLoadBalancedRestTemplate")
private RestTemplate myLoadBalancedRestTemplate;
/**
* 自定义负载均衡算法Test
* 返回对象为ResponseEntity对象,包含了响应体的重要信息: 响应头、响应状态码、响应体等
*
* @return R */ @GetMapping("/consumer/diyLoadBalancedTest")
public String getPaymentLoadBalanced() {
log.info("/consumer/diyLoadBalancedTest");
List<ServiceInstance> instances = discoveryClient.getInstances("cloud-payment-service");
log.info("instancesSize=" + instances.size());
if (instances == null || instances.size() <= 0) {
return null;
}
ServiceInstance serviceInstance = myLoadBalancer.instances(instances);
URI uri = serviceInstance.getUri();
log.info("uri:" + uri);
return myLoadBalancedRestTemplate.getForObject(uri + "/payment/lb/getServerPort", String.class);
}