此文章主要讲解springcloud中负责服务调用和负载均衡的工具Ribbon的相关知识。
前言
前面的章节中,我们已经实现了服务的注册和服务发现。当启动某个服务的时候,可以通过HTTP的形式将信息注册到注册中心,并且可以通过SpringCloud提供的工具获取注册中心的服务列表。但是服务之间的调用还存在很多问题:如何更加方便的调用微服务,多个微服务的提供者如何选择,如何负载均衡等等。
Ribbon基础
什么是Ribbon?
-
Ribbon是Netflix公司发布的一个负载均衡器,有助于控制HTTP和TCP的
客户端行为。在SpringCloud中,Eureka一般配合Ribbon进行使用,Ribbon提供了客户端负载均衡的功能,Ribbon利用从Eureka中读取到的服务信息,在调用服务节点提供的服务时,会合理的进行负载均衡。 -
在SpringCloud中可以将注册中心Eureka和Ribbon配合使用,Ribbon自动的从注册中心获取服务提供者的列表新,并基于内置的负载均衡算法,请求服务。
Ribbon的作用
-
服务调用:基于Ribbon实现服务调用,是通过拉取到的所有服务列表组成(服务名-请求路径的)映射关系。
借助于RestTemplate最终进行调用。 -
负载均衡:当有多个服务提供者时,Ribbon可以根据
负载均衡的算法自动的选择需要调用的服务地址。
简介




Ribbon的使用
我们这里提前启动好之前在搭建的 eureka Server 集群(5个模块)
- cloud-eureka-server7001
- cloud-eureka-server7002
- cloud-provider-payment8001
- cloud-provider-payment8002
- cloud-consumer-order80

上面在eureka时,确实实现了负载均衡机制,那是因为 netflix-eureka-client 包里面自带着 ribbon,在使用Eureka作为注册中心的时候,不需要再导入Ribbon的相关依赖,因为Eureka内部集成了Ribbon了。

RestTemplate 说明
有两种请求方式:post和get ,还有两种返回类型:object 和 Entity

RestTemplate 的 ForEntity 相比 ForObject特殊的地方:


就是说如果使用 ForObject 得到的就是提供者返回的对象,而如果要使用 ForEntity 得到时 ResponstEntity 对象,使用 getBody() 才能得到提供者返回的数据。
//使用forEnriry示例:
@GetMapping("/consumer/payment/forEntity/{id}")
public CommonResult<Payment> getPaymentById2(@PathVariable("id") Long id) {
ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL + "/payment/" + id, CommonResult.class);
if (entity.getStatusCode().is2xxSuccessful()) {
return entity.getBody();
} else {
return new CommonResult<>(444, "操作失败");
}
}
负载均衡
什么是负载均衡
-
在搭建网站的时候,如果单节点的web服务性能和可靠性都无法达到要求;或者是在使用外网服务的时候,经常担心被人攻破,一不小心就会有打开外网端口的请求,通常这个时候加入负载均衡就能有效解决服务问题。
-
负载均衡是一种基础的网络服务,其原理是通过运行在前面的负载均衡服务,按照指定的负载均衡算法,将流量分配到后端服务集群上,从而为系统提供并行扩展的能力。
-
负载均衡的应用场景包括流量包、转发规则以及后端服务,由于该服务有内外网个例、健康检查等功能,能够有效提供系统的安全性和可用性。

客户端负载均衡和服务端负载均衡
客户端负载均衡
-
客户端会有一个服务器地址列表,在发送请求前通过负载均衡算法选择一个服务器,进行访问;即在客户端进行负载均衡算法分配。
-
典型应用:Ribbon是客户端负载均衡。
服务端负载均衡
-
先发送请求到负载均衡服务器或软件,然后通过负载均衡算法,在多个服务器之间选择一个进行访问;即在服务器端进行负载均衡算法分配。
-
典型应用:
-
硬件:F5等。
-
软件:Nginx等。
Ribbon内置的负载均衡策略
Ribbon内置了多种负载均衡策略,内部负责复杂均衡的顶层接口为com.netflix.loadbalancer.IRule,实现方式如下:

Ribbon 负载均衡规则类型
(IRule接口的实现)

com.netflix.loadbalancer.RoundRobinRule:# 以轮询的方式进行负载均衡。
com.netflix.loadbalancer.RandomRule:# 随机策略。
com.netflix.loadbalancer.RetryRule:# 重试策略。
com.netflix.loadbalancer.WeightedResponseTimeRule:# 权重策略。会计算每个服务的权重,权重越高的被调用的可能性会越大。
com.netflix.loadbalancer.BestAvailableRule:# 最佳策略。遍历所有的服务实例,过滤掉故障实例,并将请求数量最小的实例返回。
com.netflix.loadbalancer.AvailabilityFilteringRule:# 可用过滤策略。过滤掉故障和请求数超过阈值的服务实例,再从剩下的实例中轮询调用。
配置负载均衡规则
Ribbon内置的负载均衡策略的使用一
在服务消费者的application.yml中修改负载均衡策略
# 修改ribbon的负载均衡策略 服务名 - ribbon - NFLoadBalancerRuleClassName :负载均衡策略
service-product:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 修改ribbon的负载均衡策略为权重策略
Ribbon内置的负载均衡策略的使用二

注意上面说的,而Springboot主启动类上的 @SpringBootApplication 注解,相当于加了@ComponentScan注解,会自动扫描当前包及子包,所以注意不要放在SpringBoot主启动类的包内。
修改 cloud-consumer-order80,新建包 myrule

在这个包下新建 MySelfRule类
@Configuration
public class MySelfRule {
@Bean
public IRule myrule(){
return new RandomRule(); //负载均衡规则定义为随机
}
}
然后在主启动类上添加注解 @RibbonClient
@EnableEurekaClient
@SpringBootApplication
//指定该负载均衡规则对哪个提供者服务使用 加载自定义规则的配置类
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class, args);
}
}
测试访问 http://localhost/consumer/payment/1 ,查看结果
Ribbon的策略选择
-
如果每个机器配置一样,建议不修改策略(推荐)。
-
如果部分机器配置强,则可以改为WeightedResponseTimeRule。
请求重试机制
在实际生产环境中,Ribbon做客户端负载均衡的时候,Ribbon默认的负载均衡算法是轮询,一旦访问到的那台微服务提供者突然宕机了,此时就会出现404的情况,这时可以使用Ribbon的请求重试机制,Ribbon的请求重试机制基于Spring的retry(Spring的重试框架)。
使用
在微服务消费者导入spring-retry的Maven坐标:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
修改微服务消费者的application.yml:
# Ribbon的重试机制
service-product:
ribbon:
# 修改ribbon的负载均衡策略 服务名 - ribbon - NFLoadBalancerRuleClassName :负载均衡策略
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 修改ribbon的负载均衡策略为权重策略
# Ribbon的重试机制参数
ConnectTimeout: 250 # Ribbon的连接超时时间
ReadTimeout: 1000 # Ribbon的数据读取超时时间
OkToRetryOnAllOperations: true # 是否对所有操作都进行重试
MaxAutoRetriesNextServer: 2 # 切换实例的重试次数
MaxAutoRetries: 1 # 对当前实例的重试次数
Ribbon中负载均衡的源码分析
参考文章:https://www.yuque.com/sunxiaping/yg511q/kxmrqz#b7025e02
轮询算法原理

手写负载算法
原理+JUC(CAS+自旋锁的复习)
启动 Eureka集群,7001和7002
8001和8002微服务改造
在这两个提供者的Controller中加入以下代码,便于测试
@GetMapping(value = "/payment/lb")
public String getPaymentLB(){
return serverPort;
}
服务提供者80改造
配置类
去掉 @LoadBalanced 注解
LoadBalancer 接口
package com.itjing.springcloud.lb;
import org.springframework.cloud.client.ServiceInstance;
import java.util.List;
public interface LoadBalancer {
ServiceInstance instances(List<ServiceInstance> serviceInstances);
}
LoadBalancer 接口实现类
package com.itjing.springcloud.lb;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class MyLB implements LoadBalancer {
private AtomicInteger atomicInteger = new AtomicInteger(0);
public final int getAndIncrement() {
int current;
int next;
do {
current = this.atomicInteger.get();
next = current >= 2147483647 ? 0 : current + 1;
} while (!this.atomicInteger.compareAndSet(current, next));
System.out.println("*******第几次访问次数,next:" + next);
return next;
}
// 负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务重启后rest接口计数从1开始
@Override
public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
int index = getAndIncrement() % serviceInstances.size();
return serviceInstances.get(index);
}
}
OrderController
加入以下内容
@Resource
private LoadBalancer loadBalancer;
@Resource
private DiscoveryClient discoveryClient;
@GetMapping(value = "/consumer/payment/lb")
public String getPaymentLB() {
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
if (instances == null || instances.size() <= 0) {
return null;
}
ServiceInstance serviceInstance = loadBalancer.instances(instances);
URI uri = serviceInstance.getUri();
return restTemplate.getForObject(uri + "/payment/lb", String.class);
}
启动访问测试
http://localhost/consumer/payment/lb
本文详细介绍了SpringCloud中Ribbon的作用,包括服务调用的简化和负载均衡策略,如轮询、随机、权重等,并演示了自定义负载均衡规则和请求重试机制。通过实例演示了Ribbon如何从Eureka获取服务列表并进行智能路由。


被折叠的 条评论
为什么被折叠?



