10

SpringCloud LoadBalancer灰度策略实现

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=MjM5MzEwODY4Mw%3D%3D&%3Bmid=2257485819&%3Bidx=1&%3Bsn=c6ba782b2b6bdd82e044cbcdf113eb61
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

如何使用 Spring Cloud 2020 中重磅推荐的负载均衡器 Spring Cloud LoadBalancer (下文简称 SCL),如何扩展负载均衡策略?你将从本文中获取到答案

快速上手 SCL

  • 如果项目中想使用 SCL,则仅需要添加如下 maven 依赖即可

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
  • SCL 是构建服务发现的基础上,由于目前 Spring Cloud Alibaba 并未兼容 SCL ( 具体兼容方案可以参考 pig [1] ),当然你可以选择使用Eureka 测试。

  • 若将 RestTemplate 和 客户端负载均衡结合使用,在 bean 定义上增加 @LoadBalanced 注解即可.

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}

个性化负载均衡策略

n2iE3qE.png!mobileSCL 内置的负载均衡策略
  • 目前版本 (spring cloud 2020) 内置轮询、随机的负载均衡策略,默认轮询策略。

  • 当然可以通过 LoadBalancerClient 注解,指定服务级别的负载均衡策略

@LoadBalancerClient(value = "demo-provider", configuration = RandomLoadbalancerConfig.class)
public class RandomLoadbalancerConfig {
@Bean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory)
{
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}

自定义负载均衡策略

  • 通过上文可知,目前 SCL 支持的负载均衡策略相较于 Ribbon 还是较少,需要开发者自行实现,好在 SCL 提供了便捷的 API 方便扩展使用。这里演示自定义一个基于注册中心元数据的灰度负载均衡策略。

  • 定义灰度负载均衡策略

@Slf4j
public class GrayRoundRobinLoadBalancer extends RoundRobinLoadBalancer {

private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;

private String serviceId;

@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get(request).next().map(serviceInstances -> getInstanceResponse(serviceInstances, request));
}

Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, Request request) {

// 注册中心无可用实例 抛出异常
if (CollUtil.isEmpty(instances)) {
log.warn("No instance available {}", serviceId);
return new EmptyResponse();
}

DefaultRequestContext requestContext = (DefaultRequestContext) request.getContext();
RequestData clientRequest = (RequestData) requestContext.getClientRequest();
HttpHeaders headers = clientRequest.getHeaders();

String reqVersion = headers.getFirst(CommonConstants.VERSION);
if (StrUtil.isBlank(reqVersion)) {
return super.choose(request).block();
}

// 遍历可以实例元数据,若匹配则返回此实例
for (ServiceInstance instance : instances) {
NacosServiceInstance nacosInstance = (NacosServiceInstance) instance;
Map<String, String> metadata = nacosInstance.getMetadata();
String targetVersion = MapUtil.getStr(metadata, CommonConstants.VERSION);
if (reqVersion.equalsIgnoreCase(targetVersion)) {
log.debug("gray requst match success :{} {}", reqVersion, nacosInstance);
return new DefaultResponse(nacosInstance);
}
}
// 降级策略,使用轮询策略
return super.choose(request).block();
}
}
  • 针对客户端注入灰度负载均衡策略

@LoadBalancerClient(value = "demo-provider", configuration = GrayRoundLoadbalancerConfig.class)
  • 服务实例定义版本号

riIrAvU.png!mobile
  • 请求携带版本号,测试使用

curl --location --request GET 'http://localhost:6060/req?key=b' \
--header 'VERSION: b'

优化负载均衡策略注入

  • 如上文所述,所有的个性化负载策略都需要手动通过 LoadBalancerClient 注入非常的不方便。我们可以参考 LoadBalancerClients 的批量注入逻辑构造自己的 BeanRegistrar
AvQNniy.png!mobile
public class GrayLoadBalancerClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar {

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
Field[] fields = ReflectUtil.getFields(ServiceNameConstants.class);

// 遍历服务名称,注入支持灰度策略的负载均衡器
for (Field field : fields) {
Object fieldValue = ReflectUtil.getFieldValue(ServiceNameConstants.class, field);
registerClientConfiguration(registry, fieldValue, GrayLoadBalancerClientConfiguration.class);
}
}
}

参考资料

[1]

兼容Spring Cloud 2020 方案: https://gitee.com/log4j/pig


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK