Spring Cloud Ribbon负载均衡服务调用
概述
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡工具。Ribbon客户端组件提供了一系列完善的配置项,如连接超时,重试等。简单地说,就是在配置文件中列出Load Balancer后面所有的机器,Ribbon会自动帮我们基于某种规则(如轮询,随机连接等)去连接这些机器,我们很容易使用Ribbon实现自定义的负载均衡算法。
Ribbon官网:https://github.com/Netflix/ribbon(维护模式)
负载均衡的表现就是,将用户的请求分摊到多个服务器上,从而达到高可用的目的。常见的负载均衡软件有:Nginx、LVS等。
Nginx是服务器端负载均衡,客户端的请求都发给Nginx,由Nginx实现分发,将请求发送到不同的服务器上。
Ribbon是客户端负载均衡,在调用微服务接口的时候,会在注册中心拿到注册信息服务列表缓存到本地JVM,在客户端通过某种规则,确定请求的链接,发送请求进行调用。
使用Ribbon负载均衡
其实在spring-cloud-starter-netflix-eureka-client坐标下,是引入了spring-cloud-starter-netflix-ribbon的,所以,只需要添加一个@LoadBalanced就可以实现负载均衡。
RestTemplate常见的方法有getForObject()、getForEntity()、postForObject()、postForEntity()方法。其中*ForObject()方法返回对象为响应体中数据转换成的对象,基本理解为JSON。*ForEntity()方法返回对象是ResponseEntity对象,包含了响应中的信息,比如响应头,响应状态码,响应体等。
新建两个模块(cloud-eureka-server7001、cloud-eureka-server7002),使用eureka实现服务注册,具体可看之前笔记,,将cloud-eureka-server7001、cloud-eureka-server7002的配置文件改成集群模式,让7001和7002互相注册,新建两个模块(cloud-provider-payment8001、cloud-provider-payment8002),也就是服务提供者,将cloud-provider-payment8001、cloud-provider-payment8002的配置文件改成集群模式,将cloud-consumer-order80的配置文件改成集群模式。新建一个服务消费者模块(cloud-consumer-order80),在cloud-consumer-order80模块中的OrderController里,添加两个方法,分别调用getForEntity()和postForEntity()方法。
注:新建模块过程省略,步骤可参考之前笔记
@GetMapping("/consumer/payment/getForEntity/{id}")
public CommonResult getPaymentById2(@PathVariable("id") Long id) {
ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);
System.out.println("status code=" + entity.getStatusCode());
System.out.println("headers=" + entity.getHeaders());
if (entity.getStatusCode().is2xxSuccessful()) {
return entity.getBody();
} else {
return new CommonResult(404, "查找失败");
}
}
@GetMapping("/consumer/payment/create2")
public CommonResult create2(Payment payment) {
return restTemplate.postForEntity(PAYMENT_URL + "/payment/create", payment, CommonResult.class).getBody();
}
先启动Eureka注册中心,再启动两个生产者模块,最后启动消费者模块,浏览器发送请求来调用ForEntity()方法进行测试。在浏览器端,可以看到port的值,不断在8001和8002之前进行切换。
Ribbon负载均衡算法
负载均衡算法:REST接口第几次请求数字%服务器集群总数量=实际调用服务器位置下标,每次重启服务器REST接口计数从1开始。找一个IRule接口的实现类,下面是RoundRobinRule类对该接口的实现。
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
log.warn("no load balancer");
return null;
} else {
Server server = null;
int count = 0;
while(true) {
// 获取server,如果获取失败,进行重试,如果10次后,还没获取到server,就报错
if (server == null && count++ < 10) {
// 获取所有状态是up的server
List<Server> reachableServers = lb.getReachableServers();
// 获取所有server
List<Server> allServers = lb.getAllServers();
int upCount = reachableServers.size();
int serverCount = allServers.size();
if (upCount != 0 && serverCount != 0) {
// 计算出要访问的server下标
int nextServerIndex = this.incrementAndGetModulo(serverCount);
server = (Server)allServers.get(nextServerIndex);
if (server == null) {
Thread.yield();
} else {
if (server.isAlive() && server.isReadyToServe()) {
return server;
}
server = null;
}
continue;
}
log.warn("No up servers available from load balancer: " + lb);
return null;
}
if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: " + lb);
}
return server;
}
}
}
private int incrementAndGetModulo(int modulo) {
int current;
int next;
// 利用cas和自旋锁,获取next的值
do {
current = this.nextServerCyclicCounter.get();
next = (current + 1) % modulo;
} while(!this.nextServerCyclicCounter.compareAndSet(current, next));
return next;
}
从上面可以发现,Ribbon核心是IRule,IRule是一个接口,它有多个实现类,分别代表不同的负载均衡策略。
IRule的子类的关系图
注:
com.netflix.loadbalancer.RoundRobinRule:轮询机制
com.netflix.loadbalancer.RandomRule:随机机制
com.netflix.loadbalancer.RetryRule:首先按照RoundRobinRule策略来获取服务,如获取服务失败,在指定时间内尝试获取服务,获取可用服务
com.netflix.loadbalancer.WeigthResponseTimeRule:是对RoundRobinRule扩展,根据响应速度进行选择,响应速度越快,优先级越高
com.netflix.loadbalancer.BestAvailableRule:首先剔除掉多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
com.netflix.loadbalancer.AvailabilityFilterRule:首先过滤掉故障实例,然后再选择并发最小的实例
com.netflix.loadbalancer.ZoneAvoidanceRule:默认规则,符合判断server所在区域的性能和server可用性选择服务器
推荐阅读
-
详解Spring Cloud负载均衡重要组件Ribbon中重要类的用法
-
Spring Cloud第七篇 | 声明式服务调用Feign
-
[Spring cloud 一步步实现广告系统] 11. 使用Feign实现微服务调用
-
spring cloud服务提供与调用示例
-
spring cloud 入门系列五:使用Feign 实现声明式服务调用
-
为何一个@LoadBalanced注解就能让RestTemplate拥有负载均衡的能力?【享学Spring Cloud】
-
撸一撸Spring Cloud Ribbon的原理-负载均衡策略
-
SpringCloud学习系列之二 ----- 服务消费者(Feign)和负载均衡(Ribbon)
-
使用spring cloud测试负载均衡时报java.lang.IllegalStateException: Request URI does not contain a valid hostname
-
spring cloud(七) 均衡负载