gateway集成nacos、loadbalancer实现自定义负载均衡器,带源码解析(cloud版本:2021.0.3|boot版本:2.7.4)[场景1:灰度发布(金丝雀发布)]

🎨领域:Java后端开发



在这里插入图片描述


🔥收录专栏: 系统设计与实战
🐒个人主页:BreezAm
💖Gitee:https://gitee.com/BreezAm
✨个人标签:【后端】【大数据】【前端】【运维】

文章目录

  • 前言
      • 二、配置步骤
        • 2.1 自定义负载均衡器
        • 2.2 编写配置类
        • 2.3 启动类添加注解
      • 三、扩展
        • 3.1 如何操作nacos中的配置文件
        • 3.2 如何操作nacos中的服务
      • 四、相关依赖

前言

在很多时候,我们需要根据自己的业务实现自定义的负载均衡,例如在灰度发布场景中(金丝雀发布),需要通过灰度策略实现负载均衡,这时候默认的负载均衡器就无法满足需求。下文主要介绍如何实现自定义负载均衡器以及相关源码解析,需要说明一下,不同的版本配置略有区别,以下是本文案例介绍的版本要求。

springbootspringloud
2.7.42021.0.3

二、配置步骤

2.1 自定义负载均衡器

  1. 通过查看源码可知要实现自己的负载均衡器,需要实现ReactorServiceInstanceLoadBalancer接口,下面的代码中,是从自带的负载均衡器RoundRobinLoadBalancer中拷贝的,因为大部分都是一样的,我们只需要关注choose(Request request)这个方法,在这里可以通过ServiceInstanceListSupplier从注册中心拿到当前访问服务的所有实例,方法要求返回的是一个服务实例,因此就可以按照自己指定的规则返回符合要求的一个实例。
/*** 灰度发布负载均衡器*/
public class GrayLoadBalancer implements ReactorServiceInstanceLoadBalancer {private static final Log log = LogFactory.getLog(RoundRobinLoadBalancer.class);final AtomicInteger position;final String serviceId;ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;public GrayLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,String serviceId) {this(serviceInstanceListSupplierProvider, serviceId, new Random().nextInt(1000));}public GrayLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,String serviceId, int seedPosition) {this.serviceId = serviceId;this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;this.position = new AtomicInteger(seedPosition);}@SuppressWarnings("rawtypes")@Overridepublic Mono<Response<ServiceInstance>> choose(Request request) {ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);return supplier.get(request).next().map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));}private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,List<ServiceInstance> serviceInstances) {Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances);if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());}return serviceInstanceResponse;}private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {if (instances.isEmpty()) {if (log.isWarnEnabled()) {log.warn("No servers available for service: " + serviceId);}return new EmptyResponse();}//此处编写自己的负载均衡策略int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;ServiceInstance instance = instances.get(pos % instances.size());return new DefaultResponse(instance);}
}
  1. 在这个版本中,我们无需增加其他配置就可以拿到HTTP请求的内容,下面选择实例的方法有个参数Request ,里面有一个上下文,从里面我们可以拿到请求的数据,在这个版本中是封装在ResponseData 实体类中的。需要说明一下,在比较老的loadbalancer版本中,如果没有做其他配置,这个Request 是空的,没有任何请求数据。
@Overridepublic Mono<Response<ServiceInstance>> choose(Request request) {
}

通过查看负载均衡客户端过滤器ReactiveLoadBalancerClientFilter源码可知,这个Request实例化的的类是DefaultRequest,里面有一个上下文类RequestDataContext,请求相关数据封装在RequestData里后放入了上下文对象里,供负载均衡器使用,相关代码如下:(注:如果需要放入自定义的数据,可以重写ReactiveLoadBalancerClientFilter类)。

DefaultRequest<RequestDataContext> lbRequest = new DefaultRequest<>(
new RequestDataContext(new RequestData(exchange.getRequest()), getHint(serviceId)));return choose(lbRequest, serviceId, supportedLifecycleProcessors).doOnNext(response -> {
...

知道了实现原理,我们就可以从自定义负载均衡器里面拿到请求数据了,案例代码如下:

 @Overridepublic Mono<Response<ServiceInstance>> choose(Request request) {DefaultRequest req = (DefaultRequest) request;RequestDataContext context = (RequestDataContext) req.getContext();RequestData requestData = context.getClientRequest();...
}

ResponseData实体类的结构如下所示。

public class ResponseData {private final HttpStatus httpStatus;private final HttpHeaders headers;private final MultiValueMap<String, ResponseCookie> cookies;private final RequestData requestData;private final Integer rawHttpStatus;...
}

2.2 编写配置类

自定义负载均衡器编写好了以后,我们就需要将其注入到spring容器中,下面是配置代码,这里会有一个坑,就是不能加注解@Configuration,因为服务是懒加载的,如果加上注解就会导致容器启动时拿不到该服务实例,出现问题,从下面可以看出,ServiceInstanceListSupplier(服务实例)是通过服务的名字拿到的。

//@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
public class GrayLoadBalancerConfiguration {@Bean@ConditionalOnMissingBeanpublic ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new GrayLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);}
}

2.3 启动类添加注解

最后一部就是在启动类上面添加注解@LoadBalancerClients,并把配置类配置上去。

@SpringBootApplication
@LoadBalancerClients(defaultConfiguration = GrayLoadBalancerConfiguration.class)
public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}
}

三、扩展

3.1 如何操作nacos中的配置文件

首先我们还是先来看下源码,部分源码如下,配置中心的配置文件是通过配置管理器NacosConfigManager 管理的,从代码中我们可以看到NacosConfigManager 是一个bean,也就是放在spring容器中管理了,因此,我们就可以在自己的业务代码中通过@Autowired将其注入就可以使用了。

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.nacos.config.enabled", matchIfMissing = true)
public class NacosConfigAutoConfiguration {...@Beanpublic NacosConfigManager nacosConfigManager(NacosConfigProperties nacosConfigProperties) {return new NacosConfigManager(nacosConfigProperties);}...
}

当我们通过以下代码注入配置管理器以后,就可以拿到nacos配置服务ConfigService了。

@Autowired
public NacosConfigManager nacosConfigManager;

在ConfigService里有对操作nacos配置文件的CRUD方法,部分接口代码如下,感兴趣的读者可以去尝试一下。

public interface ConfigService {String getConfig(String var1, String var2, long var3) throws NacosException;boolean publishConfig(String var1, String var2, String var3) throws NacosException;boolean publishConfig(String var1, String var2, String var3, String var4) throws NacosException;boolean publishConfigCas(String var1, String var2, String var3, String var4, String var5) throws NacosException;boolean removeConfig(String var1, String var2) throws NacosException;
}

3.2 如何操作nacos中的服务

有时在业务场景中,我们需要从配置中心拿到存活的服务实例。和配置管理一样,需要拿到nacos服务管理器,从以下代码可以看出NacosServiceManager 也是一个bean,操作和3.1中介绍的大同小异。

@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
@ConditionalOnNacosDiscoveryEnabled
public class NacosServiceAutoConfiguration {@Beanpublic NacosServiceManager nacosServiceManager() {return new NacosServiceManager();}
}

四、相关依赖

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

🔥收录专栏:系统设计与实战
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/517348.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高德服务单元化方案和架构实践

导读&#xff1a;本文主要介绍了高德在服务单元化建设方面的一些实践经验&#xff0c;服务单元化建设面临很多共性问题&#xff0c;如请求路由、单元封闭、数据同步&#xff0c;有的有成熟方案可以借鉴和使用&#xff0c;但不同公司的业务不尽相同&#xff0c;要尽可能的结合业…

免费直播:1小时带你体验Python车牌识别实战

Python基础学会了&#xff0c;实战又是爬虫&#xff1f;太枯燥&#xff1f;别无聊&#xff0c;CSDN学院邀请章秀淞老师开设技术直播课&#xff1a;1小时带你体验车牌识别实战。让你从众多车中&#xff0c;能用Python技术找到夏树上的那辆叔叔的奔驰车牌&#xff08;玩笑&#x…

IDEA 断点调试高级玩法 |debug高手必看!

文章目录条件表达式//条件表达式public static void main(String[] args) {for (int i 0; i < 10; i) {System.out.println(i);}} }需求1&#xff1a; 当i2时&#xff0c;断点停顿 添加断点&#xff0c;鼠标右击添加自定义条件即可 需求2&#xff1a; 当i偶数时&#xff…

听说了吗?阿里双11作战室竟1根网线都没有

双11不光是购物狂欢节&#xff0c;更是对技术的一次“大考”&#xff0c;对于阿里巴巴企业内部运营的基础保障技术而言&#xff0c;亦是如此。 回溯双11历史&#xff0c;这背后也经历过“小米加步枪”的阶段&#xff1a;作战室从随处是网线&#xff0c;交换机放地上的“一地狼…

如何给U盘设置一张妖娆又骚气的图标

夫陶公清风千古&#xff0c;余又何人&#xff0c;敢称庶几 &#x1f4a1;介绍 设置完就这样&#xff0c;看起来有些巴适&#xff0c;其实没啥用&#xff01;&#xff01; &#x1f3a8; 一、准备工作 百度上找一张妖娆的图片将图片转换成图标文件&#xff08;.ico格式&#…

最佳实践 | 数据库迁云解决方案选型 流程全解析

Oracle是非常强大的综合数据库&#xff0c;但同时也存在一些劣势&#xff0c;比如由于采用集中式架构&#xff0c;无法很好地实现横向扩展&#xff0c;并且其稳定性依赖于硬件。出于架构升级、降低成本和云化等需求&#xff0c;越来越多的企业需要“去Oracle”。 日前&#xff…

Centos7.x 在线安装Docker和离线安装 Docker

文章目录一、在线安装二、联网下载软件2.1. 下载rpm2.2. 安装 docker2.3.测试验证一、在线安装 sudo yum install -y yum-utils device-mapper-persistent-data lvm2 curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyunsystemctl enable docker systemctl s…

没错!Python杀死了Excel!

月前&#xff0c;日本最大的证券公司之一野村证券首席数字官马修汉普森&#xff0c;在Quant Conference上发表讲话&#xff1a;“用Excel的人越来越少&#xff0c;大家都在码Python代码。”甚至直接说&#xff1a;“Python在数据分析领域正在超越Excel&#xff01;”作为一门胶…

给 K8s API “做减法”:阿里巴巴云原生应用管理的挑战和实践

早在 2011 年&#xff0c;阿里巴巴内部便开始了应用容器化&#xff0c;当时最开始是基于 LXC 技术构建容器&#xff0c;然后逐渐切换到 Docker&#xff0c;自研了大规模编排调度系统。到了 2018 年&#xff0c;我们团队依托 K8s 体系开始推进“轻量级容器化”&#xff0c;同时投…

VMware 快照

文章目录1. 拍摄快照2. 将此虚拟机恢复到快照3. 管理此虚拟机的快照1. 拍摄快照 2. 将此虚拟机恢复到快照 3. 管理此虚拟机的快照

基于OOS批量修改资源标签值

场景 当资源上有个标签键: 部门&#xff0c;标签值&#xff1a;零售&#xff0c;由于部门资源整合要把标签&#xff08;键: 部门&#xff0c;值&#xff1a;零售&#xff09;修改为标签&#xff08;键: 部门&#xff0c;值&#xff1a;新零售&#xff09;&#xff0c;如何快速…

首度公开!OceanBase存储系统架构的演进历程及工程实践

随着用户数据量的不断增长&#xff0c;基于传统共享存储的纵向扩展能力渐渐变得力不从心&#xff0c;分布式存储成为应对用户海量数据的标配。 作为一位架构师&#xff0c;在设计系统的分布式存储架构时&#xff0c;需要关注哪些方面呢&#xff1f;或者我们换句话说&#xff0c…

Java“拍了拍”你,面试其实没那么难...

后疫情时代&#xff0c;连程序员这个多金的职业也遭受到了一定程度的打击。从各大招聘网站和多次面试经历中&#xff0c;相信大家已经意识到&#xff0c;面试官对程序员技能体系和项目经验考核似乎更严苛了。你在面试中常常为什么苦恼呢&#xff1f;简历撰写&#xff1f;数据算…

双 11 模块 79.34% 的代码是怎样智能生成的?

导读&#xff1a;作为今年阿里经济体前端委员会的四大技术方向之一&#xff0c;前端智能化方向一被提及&#xff0c;就不免有人好奇&#xff1a;前端结合 AI 能做些什么&#xff0c;怎么做&#xff0c;未来会不会对前端产生很大的冲击等等。本篇文章将围绕这些问题&#xff0c;…

在 xfire中使用 GZip来 压缩 传输量

在XFire1.2以后支持SOAP消息的GZip压缩传输&#xff0c;在合适的地方启动GZip压缩可以减少网络传输的数据量&#xff0c;加快速度。 文章目录1. jar下载地址2. 服务端3.客户端1. jar下载地址 https://sourceforge.net/projects/pjl-comp-filter/ 或者直接点击下载最新版本&am…

从零开始入门 | Kubernetes 中的服务发现与负载均衡

一、需求来源 为什么需要服务发现 在 K8s 集群里面会通过 pod 去部署应用&#xff0c;与传统的应用部署不同&#xff0c;传统应用部署在给定的机器上面去部署&#xff0c;我们知道怎么去调用别的机器的 IP 地址。但是在 K8s 集群里面应用是通过 pod 去部署的&#xff0c; 而 …

浅谈分布式存储中的网络通信

作者 | 火尉子责编 | Carol封图 | CSDN 下载自视觉中国在各行各业数字化转型深入的当下&#xff0c;数据呈爆炸式增长。面对海量数据的存储需求&#xff0c;分布式存储显然在架构上有着天然优势&#xff0c;但在这波数据洪流之中也面临着性能上的全新挑战。由于分布式存储的工作…

我们和全球的朋友一起回家

昨天上午9点30分&#xff0c;阿里巴巴集团&#xff08;09988.HK&#xff09;正式在港交所挂牌交易&#xff0c;成为首个同时在美股和港股两地上市的中国互联网公司。 这次&#xff0c;港交所的“C位”站了一排普通人——阿里巴巴从四大洲8个国家请来敲锣的客户代表。他们是全球…

为什么你的高效交付,却没有好的业务成果?

作者| 彭鑫(公亮) 出品|阿里巴巴新零售淘系技术部 11月中旬&#xff0c;作者在 TOP 100 案例和人人都是产品经理的两次大会上分别进行了两场关于价值交付的分享&#xff0c;结合分享后的反馈焦点&#xff0c;立足业务整体交付的价值最大化&#xff0c;特产此文。 持续需求交付…

KAFKA 最新版 单机安装、配置、部署(linux环境)

文章目录一、基础软件下载1. JDK2. KAFKA3. ZOOKEEPER二、zookeeper配置启动2.1. 解压2.2. 配置环境变量2.3. 刷新环境变量2.4. 配置文件调整2.4. 启动zookeeper2.5. 监控zk启动状态2.6. 查看日志三、kafka配置启动3.1. 解压3.2. 配置环境变量3.3. 刷新环境变量3.4. 验证是否生…