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

news/2024/12/5 12:40:56/

背景:

当我们使用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.ppmy.cn/news/13077.html

相关文章

【4 - 降维算法PCA和SVD - 原理部分】菜菜sklearn机器学习

课程地址&#xff1a;《菜菜的机器学习sklearn课堂》_哔哩哔哩_bilibili 第一期&#xff1a;sklearn入门 & 决策树在sklearn中的实现第二期&#xff1a;随机森林在sklearn中的实现第三期&#xff1a;sklearn中的数据预处理和特征工程第四期&#xff1a;sklearn中的降维算法…

python textwrap 模块,这里有你需要的学习资料

Python 标准库中的 textwrap 模块可以把长文本按照指定的宽度进行换行。这个模块提供了一些高级的文本处理功能&#xff0c;例如按照指定的宽度进行自动换行&#xff0c;保留段首缩进等。 python textwrap 模块python textwrap 模块主要函数textwrap.wrap(text, width70, \*\*k…

Xpath Helper 在新版Edge中的安装及解决快捷键冲突问题

&#x1f935;‍♂️ 个人主页老虎也淘气 个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f44d;&#x1f3fb; 收藏…

ESP32 (WIFI)-AP、STA模式(13)

提示&#xff1a;本博客作为学习笔记&#xff0c;有错误的地方希望指正 文章目录一、ESP32 WIFI模式概述二、ESP32 WIFI-AP初始化流程三、WIFI-AP示例四、ESP32 WIFI-STA初始化流程五、WIFI-STA示例一、ESP32 WIFI模式概述 参考资料&#xff1a;ESP IDF编程手册V4.4   WIFI主…

Linux 环境部署 Nexus 服务

一 私服是什么&#xff1f; 一个特殊的远程仓库&#xff0c;它是架设在局域网内的仓库服务&#xff0c;供局域网内的开发人员使用。 当Maven需要下载构建的使用&#xff0c; 它先从私服请求&#xff0c;如果私服上没有的话&#xff0c;则从外部的远程仓库下载&#xff0c;然后…

JupyterLab,极其强大的 10 个秘密技巧

之前一篇文章&#xff1a;整理了上千个 Python 工具库&#xff0c;涵盖24个大方向 没想到火了。喜欢的可以看一下。 今天我给大家分享一下 Jupyter Lab 的一些内容。 JupyterLab 是 Jupyter 主打的最新数据科学生产工具&#xff0c;某种意义上&#xff0c;它的出现是为了取代…

【opencv】二维面找角点/关键点 实现

every blog every motto: You can do more than you think. 0. 前言 二维面找角点/关键点 实现 1. 正文 1.1 前提 1.1.1 显示函数 def show(arr):plt.imshow(arr)plt.show()def cvshow(arr):cv2.namedWindow(a, 0)cv2.imshow(a, arr)cv2.waitKey(0)cv2.destroyAllWindows…

Web(八)

XML概念&#xff1a;Extensible Markup Language 可扩展标记语言可扩展&#xff1a;标签都是自定义的。 <user> <student>功能* 存储数据1. 配置文件2. 在网络中传输xml与html的区别1. xml标签都是自定义的&#xff0c;html标签是预定义。2. xml的语法严格&#xf…