SpringCloud LoadBalancer灰度策略实现

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

快速上手 SCL

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


 org.springframework.cloud
 spring-cloud-starter-loadbalancer

SCL 是构建服务发现的基础上,由于目前 Spring Cloud Alibaba 并未兼容 SCL (具体兼容方案可以参考 pig[1]),当然你可以选择使用Eureka 测试。
若将 RestTemplate 和 客户端负载均衡结合使用,在 bean 定义上增加 @LoadBalanced 注解即可.

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

个性化负载均衡策略


SpringCloud LoadBalancer灰度策略实现_第1张图片

目前版本 (spring cloud 2020) 内置轮询、随机的负载均衡策略,默认轮询策略。
当然可以通过 LoadBalancerClient 注解,指定服务级别的负载均衡策略

@LoadBalancerClient(value = "demo-provider", configuration = RandomLoadbalancerConfig.class)
public class RandomLoadbalancerConfig {
 @Bean
 public ReactorLoadBalancer 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 serviceInstanceListSupplierProvider;

 private String serviceId;

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

 Response getInstanceResponse(List 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 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)

服务实例定义版本号



请求携带版本号,测试使用

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

优化负载均衡策略注入

如上文所述,所有的个性化负载策略都需要手动通过 LoadBalancerClient 注入非常的不方便。我们可以参考 LoadBalancerClients 的批量注入逻辑构造自己的 BeanRegistrar


SpringCloud LoadBalancer灰度策略实现_第2张图片
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);
  }
 }
}

你可能感兴趣的:(SpringCloud LoadBalancer灰度策略实现)