如何使用 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),当然你可以选择使用Eureka 测试。

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

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

个性化负载均衡策略

  • 目前版本 (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)
  • 服务实例定义版本号

  • 请求携带版本号,测试使用
curl --location --request GET 'http://localhost:6060/req?key=b' \
--header 'VERSION: b'

优化负载均衡策略注入

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

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);
}
}
}

>>> 源码 https://gitee.com/log4j/pig,欢迎署名转载 <<<

最新文章

  1. Atitit图像处理的用途
  2. Eclipse调试常用技巧
  3. 躲避大龙(codevs 1961)
  4. php正则获取网页标题、关键字、网页描述代码
  5. mysql存储emoji表情
  6. Java基础-静态代理与动态代理比较
  7. POJ 2486 Apple Tree(树形DP)
  8. 周赛-Expression 分类: 比赛 2015-08-02 09:35 3人阅读 评论(0) 收藏
  9. boot/setup.S
  10. 【HNOI2004】【P1365】L语言
  11. 关于Filezilla是否支持sftp
  12. 【转】 Linux/Unix 进程间通信的各种方式及其比较
  13. 非GUI-Qt程序运行后显示Console(简单好用)
  14. ColumnEdit 数据源修改
  15. 用2D动画做遮罩
  16. An impassioned circulation of affection
  17. Java常用文件操作-2
  18. Android使用SVG矢量动画(二)
  19. vue组件通信方式总结
  20. Spring MVC中Controller如何将数据返回给页面

热门文章

  1. 「NGK每日快讯」12.18日NGK公链第45期官方快讯!
  2. DeFi 热潮下,NGK将成为下一个财富密码
  3. 【PY从0到1】 一文掌握Pandas量化基础
  4. Fast R-CNN训练自己的数据集时遇到的报错及解决方案
  5. pycharm + git+gitlab的可视化界面操作
  6. eclipse中将项目加载到tocat报错:Tomcat version 6.0 only supports J2EE 1.2, 1.3, 1.4, and Java EE 5 Web modules
  7. 微信小程序:添加全局的正在加载中图标效果
  8. RabbitMQ(三) SpringBoot2.x 集成 RabbitMQ
  9. 剑指 Offer 61. 扑克牌中的顺子 + 简单题 + 思维
  10. 使用Groovy构建DSL