Ribbon真的会被Spring Cloud Loadbalancer替代吗?
Spring Cloud
版本如果Hoxton.M2 RELEASED
版本之前的,Nacos Discovery
默认是集成了Ribbon
的,但是最新Alibaba-Nacos-Discovery
在Hoxton.M2 RELEASED
版本之后弃用了Ribbon
,使用Spring Cloud Loadbalancer
作为客户端的负载均衡组件。从Spring Cloud 2020版本开始,Spring Cloud移除了 Ribbon,使用Spring Cloud Loadbalancer作为客户端的负载均衡组件。
Nacos最新版本
1️⃣ Nacos 2021
版本已经没有自带Ribbon
的整合,所以需要引入另一个支持的jar
包loadbalancer
;
2️⃣ Nacos 2021
版本已经取消了对Ribbon
的支持,所以无法通过修改Ribbon
负载均衡的模式来实现Nacos
提供的负载均衡模式。
使用nacos 2021.1版本实现负载均衡
2021.0.1
版本的nacos-discovery
移除了Ribbon
依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2021.1</version>
</dependency>
这个包已经移除了Ribbon
支持,如果需要实现负载均衡,官方推荐替代方案是采用Spring Cloud LoadBalancer
,对应的依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
Spring Cloud LoadBalancer
Spring Cloud LoadBalancer发展渊源:
2017
年Spring
开始尝试开发新的项目Spring Cloud LoadBalancer
替代Ribbon
,项目托管在spring-cloud-incubator孵化器- 经过N个月的时间,
Spring
突然将项目标记归档迁移到了Spring Cloud Common - 目前
Spring Cloud Common
最新版本为已经迭代至4.0.1
。
Spring Cloud
提供了自己的客户端负载均衡器抽象和实现。对于负载均衡机制,增加了ReactiveLoadBalancer
接口,并提供了基于Round-Robin
和Random
的实现。为了从响应式服务中选择实例,使用了ServiceInstanceListSupplier
。目前,我们支持基于服务发现的ServiceInstanceListSupplier
实现,该实现使用类路径中可用的发现客户端从服务发现中检索可用实例。
可以通过设置
spring.cloud.loadbalancer.enabled
的值为false
来禁用Spring Cloud LoadBalancer
。
之前版本是通过以下方式实现负载均衡:
package com.jacklin.mamba.config;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import com.netflix.loadbalancer.RoundRobinRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* 配置RestTemplate
*
*/
@Configuration
public class RestTemplateConfig {
/**
* 为restTemplate整合ribbon
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
/**
* Ribbon 自带的负载均衡策略有如下几个:
* 1.AvailabilityFilteringRule: 先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,以及并发连接数超过阈值的服务,剩下的服务,使用轮询策略
* 2.BestAvailableRule: 先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
* 3.ZoneAvoidanceRule: 复合判断 server 所在区域的性能和 server 的可用性选择服务器
* 4.RandomRule: 随机负载均衡
* 5.RoundRibbonRule:轮询,人人有份
* 6.RetryRule:先轮询,如果获取失败则在指定时间内重试,重新轮询可用的服务
* 7.WeightedResponseTimeRule:根据平均响应时间计算所有服务的权重,响应越快的服务权重越高,越容易被选中。
*/
@Bean
public IRule myCustomRule() {
//轮询策略负载均衡算法,人人有份
return new RoundRobinRule();
}
}
在注册bean
的同时,添加@LoadBalanced
负载均衡注解,赋予RestTemplate
负载均衡能力,默认的负载均衡算法是:轮训方式。
Spring Cloud LoadBalancer原理
LoadBalancerClient
LoadBalancerClient
作为负载均衡客户端,用于进行负载均衡逻辑,从服务列表中选择出一个服务地址进行调用,其内部方法为:
public interface LoadBalancerClient extends ServiceInstanceChooser {
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
<T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
URI reconstructURI(ServiceInstance instance, URI original);
}
在LoadBalancerClient
中存在两个executor()
方法,都是用于执行请求的,reconstructURI()
方法用来重构URL
,LoadBalancerClient
的默认实现类为BlockingLoadBalancerClient
,BlockingLoadBalancerClient
对象中存在两个choose()
方法,分别实现重写了ServiceInstanceChooser
的两个choose()
方法。
ReactiveLoadBalancer
上图可以看出,ReactorLoadBalancer
即可继承ReactiveLoadBalancer
接口,默认情况下使用的ReactiveLoadBalancer
实现是RoundRobinLoadBalancer
。要切换到不同的实现,无论是选择的服务还是所有服务,都可以使用自定义LoadBalancer
配置机制,通过查看源码,ReactiveLoadBalancer
提供了choose
方法:
public interface ReactorLoadBalancer<T> extends ReactiveLoadBalancer<T> {
/**
* Choose the next server based on the load balancing algorithm.
*/
@SuppressWarnings("rawtypes")
Mono<Response<T>> choose(Request request);
default Mono<Response<T>> choose() {
return choose(REQUEST);
}
}
ReactorServiceInstanceLoadBalancer
ReactorServiceInstanceLoadBalancer
作为ReactiveLoadBalancer
的实现,默认提供了两种不同的负载均衡器,分别是:RandomLoadBalancer
(随机负载均衡器)和RoundRobinLoadBalancer
(轮询负载均衡器),在需要自定义负载均衡规则的时候我们只需要通过实现ReactorServiceInstanceLoadBalancer
,重写choose
方法即可。
接下来,需要在新的版本上实现RestTemplate
负载均衡策略调用,结合Spring官方案例,需要自定义LoadBalancer
配置机制来实现负载均衡,首先自定义负载均衡策略配置:
- 自定义负载均衡策略CustomLoadBalancerConfiguration
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
/**
* Spring Cloud LoadBalancer替代Ribbon实现 随机/轮训 方式负载均衡策略配置
*
* @author: austin
* @since: 2023/2/4 15:14
*/
public class CustomLoadBalancerConfiguration {
/**
* 自定义负载均衡策略(随机/轮训)
*
* @return ReactorLoadBalancer
*/
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory factory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(factory.getLazyProvider(name, ServiceInstanceListSupplier.class), name); //随机
//return new RoundRobinLoadBalancer(factory.getLazyProvider(name, ServiceInstanceListSupplier.class), name); // 轮训
}
}
接着在RestTemplate配置上通过@LoadBalancerClient指定注入对应策略配置:
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* 配置我们自定义的LoadBalancer策略,注:这里的类为注入Bean的类,而非负载均衡的实现类
*
* @author austin
* @date 2023/2/4 10:23
*/
@Configuration
@LoadBalancerClients(defaultConfiguration = {CustomLoadBalancerConfiguration.class})
public class CustomRestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
在内容中心服务启动类添加:@LoadBalancerClient(value = "CLOUD-CONTENT-CENTER-SERVICE", configuration = CustomRestTemplateConfig.class)
/**
* 内容中心服务启动类
*
* @author austin
*/
@MapperScan(basePackages = "com.jacklin.mamba.mapper")
@SpringBootApplication
@EnableFeignClients
@LoadBalancerClient(value = "CLOUD-CONTENT-CENTER-SERVICE", configuration = CustomRestTemplateConfig.class)
public class MambaContentCenterApplication {
public static void main(String[] args) {
SpringApplication.run(MambaContentCenterApplication.class, args);
}
}
✔此时,RestTemplate
已经具备了负载均衡器的能力,采用的负载均衡策略为:轮训方式负载均衡,下面我以内容中心调用用户中心为例,测试Spring Cload LoadBalancer
的轮训负载均衡策略是否生效,目前内容中心服务实例地址为:http://localhost:9081 用户中心服务分别启动了两个实例,实例1:http://localhost:8081,实例2:http://localhost:8082,现在内容中心通过调用用户中心获取用户信息,对应的Nacos注册的实例信息如下图所示:
访问:http://localhost:9081/post/1 , 查看内容中心控制台:
2023-02-10 01:39:19.435 DEBUG 29144 --- [io-9081-exec-10] c.j.m.feignClient.UserCenterFeignClient : [UserCenterFeignClient#findById] ---> GET http://user-center/users/1 HTTP/1.1
2023-02-10 01:39:19.435 DEBUG 29144 --- [io-9081-exec-10] c.j.m.feignClient.UserCenterFeignClient : [UserCenterFeignClient#findById] ---> END HTTP (0-byte body)
2023-02-10 01:39:19.440 DEBUG 29144 --- [io-9081-exec-10] c.j.m.feignClient.UserCenterFeignClient : [UserCenterFeignClient#findById] <--- HTTP/1.1 200 (5ms)
2023-02-10 01:39:19.441 DEBUG 29144 --- [io-9081-exec-10] c.j.m.feignClient.UserCenterFeignClient : [UserCenterFeignClient#findById] connection: keep-alive
2023-02-10 01:39:19.441 DEBUG 29144 --- [io-9081-exec-10] c.j.m.feignClient.UserCenterFeignClient : [UserCenterFeignClient#findById] content-type: application/json
2023-02-10 01:39:19.441 DEBUG 29144 --- [io-9081-exec-10] c.j.m.feignClient.UserCenterFeignClient : [UserCenterFeignClient#findById] date: Thu, 09 Feb 2023 17:39:19 GMT
2023-02-10 01:39:19.441 DEBUG 29144 --- [io-9081-exec-10] c.j.m.feignClient.UserCenterFeignClient : [UserCenterFeignClient#findById] keep-alive: timeout=60
2023-02-10 01:39:19.441 DEBUG 29144 --- [io-9081-exec-10] c.j.m.feignClient.UserCenterFeignClient : [UserCenterFeignClient#findById] transfer-encoding: chunked
2023-02-10 01:39:19.441 DEBUG 29144 --- [io-9081-exec-10] c.j.m.feignClient.UserCenterFeignClient : [UserCenterFeignClient#findById]
2023-02-10 01:39:19.441 DEBUG 29144 --- [io-9081-exec-10] c.j.m.feignClient.UserCenterFeignClient : [UserCenterFeignClient#findById] {"id":1,"wechat":"jacklin0828","nickname":"austin","roles":"admin","avatar":"https://w.wallhaven.cc/full/6o/wallhaven-6o6orw.png","createTime":"2021-12-16","updateTime":"2021-12-16","bonus":300}
2023-02-10 01:39:19.441 DEBUG 29144 --- [io-9081-exec-10] c.j.m.feignClient.UserCenterFeignClient : [UserCenterFeignClient#findById] <--- END HTTP (194-byte body)
观察用户中心服务的日志打印,不同实例分别被调用了5次:
说明使用Spring Cloud Balancer配置的负载均衡规则已经生效👏👏👏...
🤔思考:
如果我们想实现 同集群下优先调用 的负载均衡算法,基于上面两种设计思想,我提供了具体实现思路如下:
同集群下优先调用
- 自定义负载均衡器
NacosSameClusterPreferenceLoadBalancer
- 利用
NacosLoadBalancer
自带的负载均衡策略(推荐)
实现思路:
- 先找到指定服务的所有实例
instanceA
- 过滤出相同集群的所有实例
instanceB
- 如果发现同集群的
instanceB为空
,才跨集群调用instanceA
- 基于权重的负载均衡,返回1个可调用的健康实例。
总结
Spring Cloud LoadBalancer
与Spring Cloud Ribbon
通过RestTemplate
做负载均衡,他们之前的对比如下:
1️⃣ 都是使用LoadBalancerInterceptor作为RestTemplate的拦截器。 2️⃣ 在LoadBalancerInterceptor中持有LoadBalancerClient对象,在
Spring Cloud LoadBalancer
中是BlockingLoadBalancerClient
,在Spring Cloud Ribbon
中是RibbonLoadBalancerClient
。 3️⃣LoadBalancerClient
中持有NamedContextFactory对象,在Spring Cloud LoadBalancer
中是LoadBalancerClientFactory,在Spring Cloud Ribbon
中是SpringClientFactory。 4️⃣Spring Cloud LoadBalancer
通过实现ReactorServiceInstanceLoadBalancer接口自定义负载均衡器,Spring Cloud Ribbon
通过实现ILoadBalancer接口。 5️⃣Spring Cloud LoadBalancer
通过注解@LoadBalancerClient
或@LoadBalancerClients
实现自定义配置,Spring Cloud Ribbon
也可以使用这两个注解,另外还可以使用@RibbonClient
或@RibbonClients
。 6️⃣Spring Cloud LoadBalancer
支持响应式编程负载均衡,即结合Spring Web Flux使用,Spring Cloud Ribbon
是不支持的。 7️⃣ 目前Ribbon
提供的负载均衡算法实现较Spring Cloud LoadBalancer
更丰富。
好了,本篇文章介绍到此结束了,如果文章内容对你有所帮助,欢迎点赞👍+评论💬+收藏❤,我是:👨🎓austin流程枫,我们下期见👏👏👏~
转载自:https://juejin.cn/post/7199000755871858744