Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于NetflixRibbon实现。
通过SpringCloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。
Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的。
Ribbon 是一个进程间通信的客户端库,并且已经在云环境下经过大量的生产测试。它提供了以下功能:
相比来说,客户端级别的负载均衡可以有更好的性能,因为不需要多经过一层代理服务器。并且,服务端级别的负载均衡需要额外考虑代理服务的高可用,以及请求量较大时的负载压力。因此,在微服务场景下,一般采用客户端级别的负载均衡为主。
对于客户端的负载均衡来说,最好搭配注册中心一起使用。这样,服务实例的启动和关闭,可以向注册中心发起注册和取消注册,保证能够动态的通知到客户端。
以下是基于Nacos
注册中心的案例,nacos
可以参考
https://blog.csdn.net/weixin_38650898/article/details/106796136
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
bootstarp.yml
配置文件spring:
application:
name: cloud-provider
profiles:
active: dev
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #Nacos服务注册中心地址
config:
server-addr: 127.0.0.1:8848 #Nacos作为配置中心地址
file-extension: yaml #指定yaml格式的配置
group: DEFAULT_GROUP
# namespace: ad6f2982-2caa-47b0-a02d-95e3b8a54594
server:
port: 8084
/**
* @author :xiao7
* @date :Created in 2020/7/8 20:38
* @description :服务提供者测试控制器
*/
@RestController
@RequestMapping("provider")
public class ProviderController {
@Value("${server.port}")
private String port;
@GetMapping("/index/{name}")
public String testProvider(@PathVariable("name") String name) {
return name + "端口号:" + port;
}
}
@EnableDiscoveryClient
@SpringBootApplication
public class ProviderApplication8083 {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication8083.class, args);
}
}
bootstrap.yml
配置文件spring:
application:
name: cloud-consumer
profiles:
active: dev
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #Nacos服务注册中心地址
config:
server-addr: 127.0.0.1:8848 #Nacos作为配置中心地址
file-extension: yaml #指定yaml格式的配置
group: DEFAULT_GROUP
# namespace: ad6f2982-2caa-47b0-a02d-95e3b8a54594
server:
port: 8082
@LoadBalanced
注解,声明 RestTemplate Bean
被配置使用 Spring Cloud LoadBalancerClient
(负载均衡客户端),实现在请求目标服务时,能够进行负载均衡。
@Configuration
public class CloudConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
//Http的String转换器编码设置
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
return restTemplate;
}
}
/**
* @author :xiao7
* @date :Created in 2020/7/8 20:38
* @description :服务提供者测试控制器
*/
@RestController
@RequestMapping("consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/index/{name}")
public String testConsumer(@PathVariable("name") String name) {
return restTemplate.getForObject("http://cloud-provider/provider/index/" + name, String.class);
}
}
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerApplication8082 {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication8082.class, args);
}
}
Ribbon 内置了 7 种负载均衡规则,如下图所示:
每个负载均衡规则说明如下:
策略 | 策略描述 |
---|---|
RandomRule | 随机选择一个 server |
RoundRobinRule | 轮询选择 server |
ZoneAvoidanceRule | 使用 ZoneAvoidancePredicate 和 AvailabilityPredicate 来判断是否选择某个 server。ZoneAvoidancePredicate 判断判定一个 zone 的运行性能是否可用,剔除不可用的 server;AvailabilityPredicate 用于过滤掉连接数过多的 server |
BestAvailableRule | 选择一个最小并发请求的 server |
AvailabilityFilteringRule | 过滤掉那些因为一直连接失败的被标记为 circuit tripped 的后端 server,并过滤掉那些高并发的的后端 server(activeConnections 超过配置的阈值) |
WeightedResponseTimeRule | 根据 server 的响应时间分配一个 weight,响应时间越长,weight 越小,被选中的可能性越低 |
RetryRule | 对选定的负载均衡策略机上重试机制 |
继承AbstractLoadBalancerRule
/**
* 每个服务调用五次
*
* @author xiao7
*/
public class FiveTimeRule extends AbstractLoadBalancerRule {
private AtomicInteger nextServerCyclicCounter;
private AtomicInteger currentCount;
public FiveTimeRule() {
nextServerCyclicCounter = new AtomicInteger(0);
currentCount = new AtomicInteger(1);
}
public FiveTimeRule(ILoadBalancer lb) {
this();
setLoadBalancer(lb);
}
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;
while (true) {
List<Server> reachableServers = lb.getReachableServers();
List<Server> allServers = lb.getAllServers();
int upCount = reachableServers.size();
int serverCount = allServers.size();
if ((upCount == 0) || (serverCount == 0)) {
return null;
}
if (currentCount.get() <= 5) {
server = allServers.get(nextServerCyclicCounter.get());
} else {
int nextServerIndex = incrementAndGetModulo(serverCount);
server = allServers.get(nextServerIndex);
}
if (server == null) {
Thread.yield();
continue;
}
if (server.isAlive() && (server.isReadyToServe())) {
if (currentCount.get() >= 5) {
currentCount.compareAndSet(currentCount.get(), 1);
} else {
currentCount.incrementAndGet();
}
return (server);
}
}
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
private int incrementAndGetModulo(int modulo) {
for (; ; ) {
int current = nextServerCyclicCounter.get();
int next = (current + 1) % modulo;
if (nextServerCyclicCounter.compareAndSet(current, next)) {
return next;
}
}
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
通过在配置文件中,添加 {clientName}.ribbon.{key}={value} 配置项,设置指定名字的 Ribbon 客户端的指定属性的值。如此,我们就可以实现 Ribbon 客户端级别的自定义配置。
修改 配置文件,额外添加如下配置:
cloud-provider:
ribbon:
NFLoadBalancerRuleClassName: xiao7.cloud.consumer.FiveTimeRule # 自定义规则
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 使用已有的其他规则
在启动类中加入@RibbonClient(value = "cloud-provider", configuration = FiveTimeRule.class)
/**
* @author xiao7
*/
@EnableDiscoveryClient
@SpringBootApplication
@RibbonClient(value = "cloud-provider", configuration = FiveTimeRule.class)
public class ConsumerApplication8082 {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication8082.class, args);
}
}
然后启动两个服务提供者,一个消费者测试~~
默认配置下,Ribbon 客户端是在首次请求服务时,才创建该服务的对应的 Ribbon 客户端。
可通过下面的配置,提前创建 Ribbon客户端。
ribbon:
# Ribbon 饥饿加载配置项,对应 RibbonEagerLoadProperties 配置类
eager-load:
enabled: true # 是否开启饥饿加载。默认为 false 不开启
clients: user-provider # 开启饥饿加载的 Ribbon 客户端名字。如果有多个,使用 , 逗号分隔。
Ribbon 只负责服务实例的选择,提供负载均衡的功能,而服务的 HTTP 调用则是交给 RestTemplate 来完成。实际上,Ribbon 也是提供 HTTP 调用功能的。
Ribbon 中,提供了 3 种 HTTP 客户端,如下:
ribbon:
okhttp:
enabled: true # 设置使用 OkHttp,对应 OkHttpRibbonConfiguration 配置类
restclient:
enabled: true # 设置使用 Jersey Client,对应 RestClientRibbonConfiguration 配置类
httpclient:
enabled: true # 设置使用 Apache HttpClient,对应 HttpClientRibbonConfiguration 配置类
Ribbon超时和重试配置
spring:
cloud:
loadbalancer:
retry: true
ribbon:
ConnectTimeout: 1000 # 请求的连接超时时间,单位:毫秒。默认为 1000
ReadTimeout: 1 # 请求的读取超时时间,单位:毫秒。默认为 1000
OkToRetryOnAllOperations: true # 是否对所有操作都进行重试,默认为 false。
MaxAutoRetries: 0 # 对当前服务的重试次数,默认为 0 次。
MaxAutoRetriesNextServer: 1 # 重新选择服务实例的次数,默认为 1 次。注意,不包含第 1 次哈。