在 2020 年以前的 SpringCloud 采用 Ribbon 作为负载均衡,但是 2020 年之后,SpringCloud 吧 Ribbon 移除了,而是使用自己编写的 LoadBalancer 替代.
1.排除客户端和openfeign中的ribbon包
java
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
2.引入scl依赖
java
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
到这里已经可以使用了,默认的负载方式就是轮询,那么如何自定义负载均衡策略呢
LoadBalancer默认提供了两种负载均衡策略:
- RandomLoadBalancer - 随机分配策略
- (默认) RoundRobinLoadBalancer - 轮询分配策略
现在希望修改默认的负载均衡策略为随机分配策略,就需要创建随机分配策略的配置类(不用加 @Configuration):
java
//这里不用加 @Configuration 注解
public class LoadBalancerConfig {
//将官方提供的 RandomLoadBalancer 注册为Bean
@Bean
public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory){
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}
通过 @LoadBalancerClient(value = "服务名", configuration = LoadBalancerConfig.class) 指定负载均衡策略为随机.
java
@FeignClient("article")
@LoadBalancerClient(value = "article", configuration = LoadBalancerConfig.class) //指定负载均衡策略为随机
public interface ArticleClient {
// @LoadBalanced(可以写,也可以不用写,默认所有方法都自动加 @LoadBalanced)
@GetMapping("/article/start")
String userStart();
}
自定义负载均衡策略:可用于灰度发布
java
/**
* Description:
* 自定义灰度
* 通过给请求头添加Version 与 Service Instance 元数据属性进行对比
* @author Jam
* @date 2021/6/1 17:26
*/
@Log4j2
public class VersionGrayLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
private final String serviceId;
private final AtomicInteger position;
public VersionGrayLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
this(serviceInstanceListSupplierProvider,serviceId,new Random().nextInt(1000));
}
public VersionGrayLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
String serviceId, int seedPosition) {
this.serviceId = serviceId;
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
this.position = new AtomicInteger(seedPosition);
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get(request).next()
.map(serviceInstances -> processInstanceResponse(serviceInstances,request));
}
private Response<ServiceInstance> processInstanceResponse(List<ServiceInstance> instances, Request request) {
if (instances.isEmpty()) {
log.warn("No servers available for service: " + this.serviceId);
return new EmptyResponse();
} else {
DefaultRequestContext requestContext = (DefaultRequestContext) request.getContext();
RequestData clientRequest = (RequestData) requestContext.getClientRequest();
HttpHeaders headers = clientRequest.getHeaders();
// get Request Header
String reqVersion = headers.getFirst("version");
if(StringUtils.isEmpty(reqVersion)){
return processRibbonInstanceResponse(instances);
}
log.info("request header version : {}",reqVersion );
// filter service instances
List<ServiceInstance> serviceInstances = instances.stream()
.filter(instance -> reqVersion.equals(instance.getMetadata().get("version")))
.collect(Collectors.toList());
if(serviceInstances.size() > 0){
return processRibbonInstanceResponse(serviceInstances);
}else{
return processRibbonInstanceResponse(instances);
}
}
}
/**
* 负载均衡器
* 参考 org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer#getInstanceResponse
* @author javadaily
*/
private Response<ServiceInstance> processRibbonInstanceResponse(List<ServiceInstance> instances) {
int pos = Math.abs(this.position.incrementAndGet());
ServiceInstance instance = instances.get(pos % instances.size());
return new DefaultResponse(instance);
}
}
java
/**
* Description:
* 自定义负载均衡器配置实现类
* @author javadaily
* @date 2021/6/3 16:02
*/
public class VersionLoadBalancerConfiguration {
@Bean
ReactorLoadBalancer<ServiceInstance> versionGrayLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new VersionGrayLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}
VersionLoadBalancerConfiguration配置类不能添加@Configuration注解。
在网关启动类使用注解@LoadBalancerClient指定哪些服务使用自定义负载均衡算法
通过@LoadBalancerClient(value = "auth-service", configuration = VersionLoadBalancerConfiguration.class),对于auth-service启用自定义负载均衡算法;
或通过@LoadBalancerClients(defaultConfiguration = VersionLoadBalancerConfiguration.class)为所有服务启用自定义负载均衡算法。