springcloud + nacos多环境联调、本地联调(即灰度版本)

news2024/11/23 8:53:43

背景:

当我们使用nacos为注册中心注册微服务时,想本地环境和测试环境公用一个nacos,即注册中心等基础服务共用。当我们在服务A开发时,本地服务和测试环境服务都是注册到同一个nacos,由于nacos自带负载均衡策略,调用服务时就会使用负载均衡选择服务,导致我们无法正常调试接口。这时我们可以选择使用灰度版本来进行服务的选择。

废话不多说直接开干

首先声明一点我springcloud环境如下:

<spring-boot.version>2.3.2.RELEASE</spring-boot.version>

<spring-cloud-alibaba.version>2.2.5.RELEASE</spring-cloud-alibaba.version>

<spring-cloud.version>Hoxton.SR8</spring-cloud.version>

nacos版本是2.1.4,

具体实现步骤如下:

1、我们在配置文件中添加版本头(开发环境版本dev,我本地环境设置xf)

spring.cloud.nacos.discovery.metadata.VERSION=xf

2、添加灰度服务接口

/**

* @author xf

* @Title:

* @Description:

* @date 2023/1/12 17:17

*/

public interface GrayLoadBalancer {

/**

* 根据serviceId 筛选可用服务

* @param serviceId 服务ID

* @param request 当前请求

* @param exchange 请求不到目标实例时,走(父级)默认逻辑所需的参数

* @param clientFactory 请求不到目标实例时,走(父级)默认逻辑所需的参数

* @return Mono<Response<ServiceInstance>>

*/

Mono<Response<ServiceInstance>> choose(String serviceId, ServerHttpRequest request, ServerWebExchange exchange, LoadBalancerClientFactory clientFactory);

}

3、灰度过滤器

(过滤器主要是重写选择实例的策略)

import com.digital.cnzz.gateway.service.GrayLoadBalancer;

import lombok.extern.slf4j.Slf4j;

import org.apache.http.util.Asserts;

import org.springframework.cloud.client.ServiceInstance;

import org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools;

import org.springframework.cloud.client.loadbalancer.reactive.Response;

import org.springframework.cloud.gateway.config.LoadBalancerProperties;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;

import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;

import org.springframework.cloud.gateway.support.DelegatingServiceInstance;

import org.springframework.cloud.gateway.support.NotFoundException;

import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;

import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;

import org.springframework.stereotype.Component;

import org.springframework.web.server.ServerWebExchange;

import reactor.core.publisher.Mono;

import java.net.URI;

/**

* @author xf

* @Title:

* @Description:

* @date 2023/1/12 17:16

*/

@Slf4j

@Component

public class GrayReactiveLoadBalancerClientFilter extends ReactiveLoadBalancerClientFilter {

private final static String SCHEME = "lb";

private static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10150;

private final GrayLoadBalancer grayLoadBalancer;

private LoadBalancerProperties loadBalancerProperties;

private final LoadBalancerClientFactory clientFactory;

public GrayReactiveLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory, LoadBalancerProperties loadBalancerProperties, GrayLoadBalancer grayLoadBalancer) {

super(clientFactory, loadBalancerProperties);

this.loadBalancerProperties = loadBalancerProperties;

this.clientFactory = clientFactory;

this.grayLoadBalancer = grayLoadBalancer;

}

@Override

public int getOrder() {

return LOAD_BALANCER_CLIENT_FILTER_ORDER;

}

@Override

public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

URI url = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);

String schemePrefix = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR);

// 直接放行

if (url == null || (!SCHEME.equals(url.getScheme()) && !SCHEME.equals(schemePrefix))) {

return chain.filter(exchange);

}

// 保留原始url

ServerWebExchangeUtils.addOriginalRequestUrl(exchange, url);

if (log.isTraceEnabled()) {

log.trace(ReactiveLoadBalancerClientFilter.class.getSimpleName() + " url before: " + url);

}

return choose(exchange).doOnNext(response -> {

if (!response.hasServer()) {

throw NotFoundException.create(loadBalancerProperties.isUse404(),

"Unable to find instance for " + url.getHost());

}

URI uri = exchange.getRequest().getURI();

// if the `lb:<scheme>` mechanism was used, use `<scheme>` as the default,

// if the loadbalancer doesn't provide one.

String overrideScheme = null;

if (schemePrefix != null) {

overrideScheme = url.getScheme();

}

DelegatingServiceInstance serviceInstance = new DelegatingServiceInstance(response.getServer(),

overrideScheme);

URI requestUrl = LoadBalancerUriTools.reconstructURI(serviceInstance, uri);

if (log.isTraceEnabled()) {

log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);

}

exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, requestUrl);

}).then(chain.filter(exchange));

}

/**

* 获取实例

* @param exchange ServerWebExchange

* @return ServiceInstance

*/

private Mono<Response<ServiceInstance>> choose(ServerWebExchange exchange) {

URI uri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);

Asserts.notNull(uri, "uri");

return grayLoadBalancer.choose(uri.getHost(), exchange.getRequest(),exchange,clientFactory);

}

}

4、基于客户端版本号灰度路由

import cn.hutool.core.collection.CollUtil;

import cn.hutool.core.map.MapUtil;

import cn.hutool.core.util.RandomUtil;

import com.digital.cnzz.gateway.service.GrayLoadBalancer;

import com.github.pagehelper.util.StringUtil;

import lombok.RequiredArgsConstructor;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.collections4.CollectionUtils;

import org.springframework.cloud.client.ServiceInstance;

import org.springframework.cloud.client.discovery.DiscoveryClient;

import org.springframework.cloud.client.loadbalancer.reactive.DefaultResponse;

import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;

import org.springframework.cloud.client.loadbalancer.reactive.Request;

import org.springframework.cloud.client.loadbalancer.reactive.Response;

import org.springframework.cloud.gateway.support.NotFoundException;

import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;

import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;

import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;

import org.springframework.http.server.reactive.ServerHttpRequest;

import org.springframework.stereotype.Component;

import org.springframework.web.server.ServerWebExchange;

import reactor.core.publisher.Mono;

import java.net.URI;

import java.util.List;

import java.util.stream.Collectors;

/**

* @author xf

* @Title:

* @Description:

* @date 2023/1/12 17:18

*/

@Slf4j

@RequiredArgsConstructor

@Component

public class VersionGrayLoadBalancer implements GrayLoadBalancer {

private final DiscoveryClient discoveryClient;

/**

* 根据serviceId 筛选可用服务

* @param serviceId 服务ID

* @param request 当前请求

* @param exchange 请求不到目标实例时,走(父级)默认逻辑所需的参数

* @param clientFactory 请求不到目标实例时,走(父级)默认逻辑所需的参数

* @return return Mono<Response<ServiceInstance>>

*/

@Override

public Mono<Response<ServiceInstance>> choose(String serviceId, ServerHttpRequest request, ServerWebExchange exchange, LoadBalancerClientFactory clientFactory) {

List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);

// 注册中心无实例 抛出异常

if (CollectionUtils.isEmpty(instances)) {

log.warn("No instance available for {}", serviceId);

throw new NotFoundException("No instance available for " + serviceId);

}

// 获取请求version,无则调用父类的策略调用可用实例

String reqVersion = request.getHeaders().getFirst("VERSION");

if (StringUtil.isEmpty(reqVersion)) {

//走父级默认负载均衡策略

return chooseSuper(exchange,clientFactory);

}

// 遍历可以实例元数据,若匹配则返回此实例

List<ServiceInstance> availableList = instances.stream()

.filter(instance -> reqVersion

.equalsIgnoreCase(MapUtil.getStr(instance.getMetadata(), "VERSION")))

.collect(Collectors.toList());

if (CollUtil.isEmpty(availableList)) {

//走父级默认负载均衡策略

return chooseSuper(exchange,clientFactory);

}

return Mono.just(new DefaultResponse(availableList.get(RandomUtil.randomInt(availableList.size()))));

}

public Mono<Response<ServiceInstance>> chooseSuper(ServerWebExchange exchange, LoadBalancerClientFactory clientFactory) {

//父级逻辑,具体实现可参考 org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter.choose 方法

URI uri = (URI)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);

ReactorLoadBalancer<ServiceInstance> loadBalancer = (ReactorLoadBalancer)clientFactory.getInstance(uri.getHost(), ReactorLoadBalancer.class, new Class[]{ServiceInstance.class});

if (loadBalancer == null) {

throw new NotFoundException("No loadbalancer available for " + uri.getHost());

} else {

return loadBalancer.choose(this.createRequest());

}

}

private Request createRequest() {

return ReactiveLoadBalancer.REQUEST;

}

}

5、改造前端代码,请求头(Request Headers)加版本号信息

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

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

相关文章

小程序开发经验分享(9)小程序快速上线汇总

微信小程序申请 开发中的Appid 需要从“微信公众平台”中获取 如果是直接从git上拉取的话 直接项目导入就可以了(名称可以是中文) 小程序基础配置 如果需要修改显示的名称和appid可以去生成的配置文件project.config.json里面修改

前端特效之毛玻璃-倾斜-日历

前端特效之毛玻璃-倾斜-日历描述项目效果index.htmlindex.css描述 项目描述开发语言HTML、JavaScript、CSS库dyCalendarJS、vanilla-tilt 该项目中需要使用到的库有&#xff1a; dyCalendarJS vanilla-tilt.js 是 JavaScript 中的一个平滑的 3D 倾斜库。vanilla-tilt dyCalen…

MS SQL Server 日志审核工具

手动审核数据库活动是一项艰巨的任务。有效实现这一目标的最佳方法是使用全面的解决方案来简化和自动化数据库和活动监控。该解决方案还应使数据库管理员能够监控、跟踪、即时识别任何操作问题的根本原因&#xff0c;并实时检测对机密数据的未经授权的访问。 审核 Microsoft S…

Bootstrap踩坑笔记(记录Bootstrap当中的核心知识点)

目录 1.Bootstrap官网 2.核心1:布局&#xff08;栅格系统&#xff09; 3.核心知识点3:响应式布局 A.容器 B.行 C.列 注意: D.案例: E.列偏移 F.列排序 4. 样式 A.按钮 B.图片 C.表格 5.组件 A.导航条 B.分页条 C.js插件 1.Bootstrap官网 Bootsrap官网 2.核心1…

docker入门(二):docker的常用命令

文章目录前言docker常用命令1 启停类/帮助 命令2镜像命令3 容器命令结语前言 大家好&#xff0c;这是我学习docker系列的笔记文章&#xff0c;目标是掌握docker,为后续学习K8s做准备。本文列举了docker的常用命令&#xff0c;感兴趣的朋友可以看一下以前的文章。 上一篇&#…

处理Springboot项目启动时streamBridge.send导致的卡住卡死问题

现象 我们的Spring Boot 项目启动时&#xff0c;偶现卡死的现象&#xff0c;启动到一半卡主不动了 2023-01-16 10:23:10.338 INFO 1 --- [ restartedMain] com.hazelcast.core.LifecycleService : [172.18.0.14]:5701 [dev] [4.2.4] [172.18.0.14]:5701 is STARTED 20…

Unity脚本 --- 常用API(常用类) --- Component类和Transform类

上面这个是Unity核心类图 第一部分 --- Component类 提供了查找组件的方法&#xff08;当前物体的&#xff0c;子物体&#xff0c;父物体的&#xff09; 1.颜色&#xff08;color&#xff09;是材质&#xff08;meterial&#xff09;控制的&#xff0c;meterial&#xff08…

【FreeRTOS】在Cortex-M4开发板上移植FreeRTOS并且实现LED灯闪烁(保姆级教程)

相信有不少小伙伴手上只有M4的开发板&#xff0c;想要移植FreeRTOS&#xff1b;但是&#xff0c;网上大部分都是M3移植教程。因此&#xff0c;陷入深深的迷茫中&#xff0c;难不成只能使用仿真了&#xff1f;&#xff1f;&#xff1f;&#x1f914;因此&#xff0c;小编特意写了…

【Linux】静态库和动态库

Linux为什么不允许普通用户给目录建立硬链接呢&#xff1f; 系统层面上有.和…硬链接指向目录。假设我们是超级用户&#xff0c;允许给目录建立硬链接&#xff0c;给根目录建立硬链接&#xff0c;从根目录开始查找&#xff0c;当查找硬链接的时候就是根目录&#xff0c;这时候递…

面试官让我聊聊 MQ 的数据丢失问题,没想到水这么深。。。

目录 一、背景引入二、Kafka分布式存储架构三、Kafka高可用架构四、画图复现Kafka的写入数据丢失问题五、Kafka的ISR机制是什么&#xff1f;六、Kafka写入的数据如何保证不丢失&#xff1f;七、总结 一、背景引入 这篇文章&#xff0c;给大家聊一下写入Kafka的数据该如何保证…

Git常用命令(全局设置获取仓库)

新建仓库: 填写名称等信息&#xff0c;根据需要选择私有&#xff0c;开源等选项。 创建完成。 邀请成员&#xff1a; Git常用命令 Git全局设置 首先要做的是设置用户名和email地址。这是非常重要的&#xff0c;每次Git提交都会使用该用户信息。 设置用户信息&#xff1a; …

岁末年初捷报频传 HashData斩获多项行业殊荣

凯歌高奏辞旧岁&#xff0c;数据赋智谱新篇。 刚刚过去的2022年&#xff0c;面对充满变数的外部环境&#xff0c;HashData始终坚持以技术为本&#xff0c;持续全面创新&#xff0c;适应数字经济发展趋势&#xff0c;笃行致远&#xff0c;砥砺前行&#xff0c;积极推动企业“上…

VPS融合怪测评脚本(主体已完善,历史遗留问题解决时间未知)(VPS fusion monster evaluation script)

ecs 原仓库链接&#xff1a;https://github.com/spiritLHLS/ecs 支持系统&#xff1a;Ubuntu 18&#xff0c;Debian 8&#xff0c;centos 7&#xff0c;Fedora&#xff0c;Almalinux 8.5, Arch 融合怪测评脚本 bash <(wget -qO- --no-check-certificate https://gitlab.…

Sealer 0.9 :帮助集群和分布式应用实现 Build、 Share、Run

作者&#xff1a;sealer 社区 新春之际&#xff0c;很高兴在此时宣布 Sealer [ 1] 0.9 版本的正式发布。Sealer 是一款致力于探索面向分布式应用的快速打包、交付和运行的解决方案。2021 年5月 Sealer 项目正式开源&#xff1b;短短一年时间&#xff0c;Sealer 在 2022 年 4 月…

人工智能图像识别四大算子

文章目录背景引入图像识别发展简介边缘检测算法*Prewitt算子**Sobel算子**Laplace算子**Conny算子** 文末寄语*背景引入 图像识别是当今计算机科学最热门的研究方向之一。随着科学技术的发展和人类社会的不断进步&#xff0c;图像识别技术在很多行业得到了广泛的应用。本章除了…

【单链表】数据结构,详解单链表,java实现代码

前言&#xff1a; 大家好&#xff0c;我是良辰丫&#x1f353;&#x1f353;&#x1f353;&#xff0c;今天我和大家一起了解一下数据结构中的链表&#xff0c;链表&#xff0c;顾名思义是用链子把一个个数据串连起了的&#xff0c;那么链表和顺序表又有什么不同呢&#xff1f;…

mysql存储过程基本语法

本文来说下mysql存储过程基本语法 文章目录基本语法使用实例变量的使用变量定义declare语句变量赋值用户变量存储过程的参数in 输入参数out 输出参数inout输入输出参数本文小结基本语法 存储过程就是具有名字的一段代码&#xff0c;用来完成一个特定的功能。创建的存储过程保存…

2022年全球白帽常用工具排行榜TOP 10

虽然此时还未到2022年年底&#xff0c;但并不妨碍我们整理一份2022年全球白帽常用的工具榜单&#xff0c;希望能给白帽们和企业安全人员们带来一定的借鉴和参考。 一方面&#xff0c;工具的重要性不言而喻&#xff0c;各大SRC的白帽们也有深刻的切身体会。一个好用且靠谱的工具…

带模糊加工时间的柔性作业车间调度理论和GA复现(python)

文章目录1.模糊作业车间1.1 模糊数1.2 三角模糊数操作1.3 模糊甘特图2.FJSP模糊加工时间GA2.1 GA算法设置2.2 python代码2.3 测试结果1.模糊作业车间 1.1 模糊数 论域X上的模糊集合A由隶属度函数u(x)表示&#xff0c;u(x)取值[0,1]。如果A为三角模糊数&#xff0c;A可以表示为…

软件测试复习07:软件测试过程

作者&#xff1a;非妃是公主 专栏&#xff1a;《软件测试》 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录测试计划测试设计测试执行测试监控测试结束软件测试过程主要有5个阶段&#xff1a;测试计划、测试设…