背景:

当我们使用微服务时,若想在本地联调就需要启动多个服务,为了避免本地启动过多服务,现将注册中心等基础服务共用。当我们在服务A开发时,都是注册到同一个nacos,这样本地和开发环境的服务A就会同时存在,当调用服务时就会使用负载均衡选择服务,导致我们无法正常调试接口。这时我们可以选择使用灰度版本来进行服务的选择。

具体实现步骤如下:

1、我们在本地配置文件中添加版本头

这样我们服务注册到nacos中点击服务列表会发现服务中都会带VERSION

spring:
cloud:
nacos:
discovery:
metadata:
VERSION: zhangsan

2、添加灰度服务接口

public interface GrayLoadBalancer {

	/**
* 根据serviceId 筛选可用服务
* @param serviceId 服务ID
* @param request 当前请求
* @return ServiceInstance
*/
ServiceInstance choose(String serviceId, ServerHttpRequest request); }

3、灰度过滤器

import lombok.extern.slf4j.Slf4j;
import org.apache.http.util.Asserts;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools;
import org.springframework.cloud.client.loadbalancer.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; @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 final LoadBalancerProperties loadBalancerProperties; public GrayReactiveLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory, LoadBalancerProperties loadBalancerProperties, GrayLoadBalancer grayLoadBalancer) {
super(clientFactory, loadBalancerProperties);
this.loadBalancerProperties = loadBalancerProperties;
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");
ServiceInstance serviceInstance = grayLoadBalancer.choose(uri.getHost(), exchange.getRequest());
return Mono.just(new DefaultResponse(serviceInstance));
} }

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

当我们调用服务带版本号时会优先匹配带版本号的服务,若找不到则会随机选择一个服务

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component; import java.util.List;
import java.util.stream.Collectors; @Slf4j
@RequiredArgsConstructor
@Component
public class VersionGrayLoadBalancer implements GrayLoadBalancer { private final DiscoveryClient discoveryClient; /**
* 根据serviceId 筛选可用服务
* @param serviceId 服务ID
* @param request 当前请求
* @return ServiceInstance
*/
@Override
public ServiceInstance choose(String serviceId, ServerHttpRequest request) {
List<ServiceInstance> instances = discoveryClient.getInstances(serviceId); // 注册中心无实例 抛出异常
if (CollUtil.isEmpty(instances)) {
log.warn("No instance available for {}", serviceId);
throw new NotFoundException("No instance available for " + serviceId);
} // 获取请求version,无则随机返回可用实例
String reqVersion = request.getHeaders().getFirst(CommonConstant.VERSION);
if (StrUtil.isBlank(reqVersion)) {
return instances.get(RandomUtil.randomInt(instances.size()));
} // 遍历可以实例元数据,若匹配则返回此实例
List<ServiceInstance> availableList = instances.stream()
.filter(instance -> reqVersion
.equalsIgnoreCase(MapUtil.getStr(instance.getMetadata(), CommonConstant.VERSION)))
.collect(Collectors.toList()); if (CollUtil.isEmpty(availableList)) {
return instances.get(RandomUtil.randomInt(instances.size()));
}
return availableList.get(RandomUtil.randomInt(availableList.size())); }
}

springcloud + nacos实现共用基础服务(灰度版本)的更多相关文章

  1. SpringCloud+Nacos实现服务配置中心(Hoxton版本)

    关于 Nacos Spring Cloud 的详细文档请参看:Nacos Config和Nacos Discovery. 通过 Nacos Server 和 spring-cloud-starter- ...

  2. 如何基于 Nacos 和 Sentinel ,实现灰度路由和流量防护一体化

    基于Alibaba Nacos和Sentinel,实现灰度路由和流量防护一体化的解决方案,发布在最新的 Nepxion Discovery 5.4.0 版,具体参考: 源码主页,请访问 源码主页指南主 ...

  3. SpringCloud实战 | 第五篇:SpringCloud整合OpenFeign实现微服务之间的调用

    一. 前言 微服务实战系列是基于开源微服务项目 有来商城youlai-mall 版本升级为背景来开展的,本篇则是讲述SpringCloud整合OpenFeign实现微服务之间的相互调用,有兴趣的朋友可 ...

  4. 朱晔的互联网架构实践心得S2E7:漫谈平台架构的工作(基础架构、基础服务、基础平台、基础中间件等等)

    前言 程序开发毕竟还不是搬砖这种无脑体力劳动,需要事先有标准,有架构,有设计,绝对不是新公司今天创立,明天就可以开始编码的.其实很多公司在起步的时候没有财力和资源建设独立的基础架构或平台架构部门,甚至 ...

  5. springcloud费话之Eureka基础

    目录: springcloud费话之Eureka基础 springcloud费话之Eureka集群 springcloud费话之Eureka服务访问(restTemplate) springcloud ...

  6. Nacos 发布 1.0.0 GA 版本,可大规模投入到生产环境

    经过 3 个 RC 版本的社区体验之后,Nacos 正式发布 1.0.0 GA 版本,在架构.功能和 API 设计上进行了全方位的重构和升级. 1.0.0 版本的发布标志着 Nacos 已经可以大规模 ...

  7. Nacos配置中心和服务的注册发现

    在上一篇中,我们已经把Nacos的集群搭建好了,那么既然已经搭建好了,就要在咱们的项目中去使用.Nacos既可以做配置中心,也可以做注册中心.我们先来看看在项目中如何使用Nacos做配置中心. Nac ...

  8. 🏆【Alibaba中间件技术系列】「Nacos技术专题」服务注册与发现相关的原理分析

    背景介绍 前几篇文章介绍了Nacos配置中心服务的能力机制,接下来,我们来介绍Nacos另一个非常重要的特性就是服务注册与发现,说到服务的注册与发现相信大家应该都不陌生,在微服务盛行的今天,服务是非常 ...

  9. Nacos源码系列—服务端那些事儿

    点赞再看,养成习惯,微信搜索[牧小农]关注我获取更多资讯,风里雨里,小农等你,很高兴能够成为你的朋友. 项目源码地址:公众号回复 nacos,即可免费获取源码 前言 在上节课中,我们讲解了客户端注册服 ...

随机推荐

  1. 用 Java 实现阻塞队列 ?

    参考 java 中的阻塞队列的内容吧,直接实现有点烦

  2. simulink仿真过程

    Simulink求解器 Simulink仿真过程 Simulink 模型的执行分几个阶段进行.首先进行的是初始化阶段,在此阶段,Simulink 将库块合并到模型中来,确定传送宽度.数据类型和采样时间 ...

  3. vue H5 超简单的swiper制作抖音上拉切换视频播放

    -----html部分------ <swiper vertical :style="{height: windowheight+'px',width:375+'px'}" ...

  4. 推荐简约漂亮的小程序插件 calendar

    公司团队制作,主要用于内部使用,觉得这个感觉不错,所以推荐出来,让大家试试~ 日历功能 日历基本功能,自定义样式 先睹为快 使用方法: 1. 在微信小程序管理后台--设置--第三方服务,按 AppID ...

  5. 利用Charles做代理测试电脑上写的H5页面

    做H5页面的同学可能经常会遇到一个场景,就是电脑上调试好的页面怎么在手机上访问测试呢? 下面就介绍一种自己经常使用的方式,利用Charles代理软件来实现! 安装Charles 直接去官网下载对应的系 ...

  6. CCF201509-2日期计算

    问题描述 给定一个年份y和一个整数d,问这一年的第d天是几月几日? 注意闰年的2月有29天.满足下面条件之一的是闰年: 1) 年份是4的整数倍,而且不是100的整数倍: 2) 年份是400的整数倍. ...

  7. EMS邮箱数据库全局监控设置

    案例任务:监控TestDB01邮箱数据库的所有邮件,监控邮箱为用户"王淑江"的邮箱. 1.EMS全局监控设置 使用PowerShell完成操作:"王淑江"监控T ...

  8. vue过滤金额自动补全小数点

    watch:{ //监听input双向绑定 balance(value) { //保留2位小数点过滤器 不四舍五入 var toFixedNum = Number(value).toFixed(3); ...

  9. [强网杯2019]upload buuoj

    提示:重点在这,可节省大部分时间 扫描后台 发现www.tar.gz备份文件. 这平台有429[太多请求限制]防护.dirsearch扫描一堆429.于是用了最笨的方法. 文件上传 先注册个账号 注册 ...

  10. Cookie&&Session&&jsp入门

    会话技术 会话:一次会话中包含多次请求和响应. 一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止 功能:在一次会话的范围内的多次请求间,共享数据 方式: 客户端会话技术:Coo ...