当前位置:网站首页>基于 GateWay 和 Nacos 实现微服务架构灰度发布方案
基于 GateWay 和 Nacos 实现微服务架构灰度发布方案
2022-06-11 14:30:00 【小毕超】
一、灰度发布
灰度发布(又名金丝雀发布)是指在黑与白之间,能够平滑过渡的一种发布方式。在其上可以进行A/B testing,即让一部分用户继续用产品特性A,一部分用户开始用产品特性B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。
灰度发布开始到结束期间的这一段时间,称为灰度期。灰度发布能及早获得用户的意见反馈,完善产品功能,提升产品质量,让用户参与产品测试,加强与用户互动,降低产品升级所影响的用户范围。
下面基于 GateWay 和 Nacos 实现微服务架构灰度发布方案,首先对生产的服务和灰度环境的服务统一注册到 Nacos 中,但是版本不同,比如生产环境版本为 1.0 ,灰度环境版本为 2.0 ,请求经过网关后,判断携带的用户是否为灰度用户,如果是将请求转发至 2.0 的服务中,否则转发到 1.0 的服务中。
二、开始实施
首先搭建两个web服务模拟生产和灰度环境,分别注册到nacos 中,注意这里服务ID 要一致:
生产环境配置:
spring:
application:
name: web
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
metadata:
version: 1.0 # 指定版本号
灰度环境配置:
spring:
application:
name: web
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
metadata:
version: 2.0 # 指定版本号
启动两个服务后,可以在nacos 中查看详情:


下面为了模拟两个服务的差异性,创建相同的接口,不同的返回:
@RestController
public class TestController {
@GetMapping("/getTest")
public String getTest(){
return "当前处于-生产环境!";
}
}
@RestController
public class TestController {
@GetMapping("/getTest")
public String getTest(){
return "当前处于-灰度环境!";
}
}
下面开始搭建 GateWay 网关,同样需要注册到 nacos 中,但是和以前不同的是,这里我们要实现一个负载均衡器,在负载均衡器中判断是否使用哪个版本的服务,这里为了演示效果,在nacos 中新建一个配置文件,将灰度用户配置在这个配置文件中,在项目中应该从 db 或 noSQL 中进行获取。

Data ID: env-config.yaml
Group: DEFAULT_GROUP
env:
gray:
version: 2.0
users: abc,ii,ss,kk,bb,pp
pro:
version: 1.0
再增加一个 GateWay 路由的配置:
Data ID:gateway.yaml
Group: DEFAULT_GROUP
spring:
cloud:
gateway:
httpclient:
connect-timeout: 2000
response-timeout: 10s
routes:
- id: web
uri: lb://web/
order: 0
predicates:
- Path=/web/**
filters:
- StripPrefix=1 # 去除请求地址中的前缀
下面搭建 gateway 网关服务,注册到 nacos 中,并加载上面创建的配置文件:
spring:
application:
name: gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
refresh-enabled: true
extension-configs[0]:
data-id: env-config.yaml
group: DEFAULT_GROUP
refresh: true
启动后,查看下是否已经注册到 nacos 中了:
测试下是否可以负载转发:


已经实现了负载效果,但是还没有达到我们想要的效果,下面开始对 gateway 网关进行修改。
首先我们新建一个 EnvProperties 来接收 env-config.yaml 中的配置,注意一定要加 @RefreshScope 注解,这样才能修改配置后通知到相应的服务:
@Data
@Configuration
@RefreshScope
public class EnvProperties {
@Value("${env.pro.version}")
private String proVersion;
@Value("${env.gray.users}")
private List<String> grayUsers;
@Value("${env.gray.version}")
private String grayVersion;
}
在创建一个 ThreadLocal ,存储当前的版本信息,这里先记下来,后面就知道什么作用了:
public class GrayscaleThreadLocalEnvironment {
private static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
public static void setCurrentEnvironment(String currentEnvironmentVsersion) {
threadLocal.set(currentEnvironmentVsersion);
}
public static String getCurrentEnvironment() {
return threadLocal.get();
}
}
下面创建 过滤器 对请求进行拦截,然后获取到用户的信息,这里就默认用户ID 在 header 中,key 为 userId,取到之后判断是否在 灰度用户列表中,如果存在就把当前的 ThreadLocal(就是上面声明的ThreadLocal ) 中存储灰度的版本号,,否则就为生产的版本号:
@Component
@RefreshScope
public class GrayscaleGlobalFilter implements GlobalFilter, Ordered {
@Autowired
EnvProperties envProperties;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
HttpHeaders header = response.getHeaders();
header.add("Content-Type", "application/json; charset=UTF-8");
List<String> list = request.getHeaders().get("userId");
if (Objects.isNull(list) || list.isEmpty()) {
return resultErrorMsg(response," 缺少userId!");
}
String userId = list.get(0);
if (StringUtils.isBlank(userId)) {
return resultErrorMsg(response," 缺少userId!");
}
if (envProperties.getGrayUsers().contains(userId)) {
//指定灰度版本
GrayscaleThreadLocalEnvironment.setCurrentEnvironment(envProperties.getGrayVersion());
} else {
//指定生产版本
GrayscaleThreadLocalEnvironment.setCurrentEnvironment(envProperties.getProVersion());
}
return chain.filter(exchange.mutate().request(request).build());
}
public int getOrder() {
return -1;
}
private Mono<Void> resultErrorMsg(ServerHttpResponse response, String msg) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", "403");
jsonObject.put("message", msg);
DataBuffer buffer = response.bufferFactory().wrap(jsonObject.toJSONString().getBytes());
return response.writeWith(Mono.just(buffer));
}
}
上面的过滤器已经标识出当前请求属于灰度还是生产,下面就需要我们重写Ribbon 负载均衡器,这里重写的 RoundRobinRule ,在 choose 方法中,根据当前 ThreadLocal 中的版本,便利服务中版本与之相等的服务,作为转发服务,为了防止服务获取失败,这里曾加了重试策略,重试 10 次还是失败,即放弃重试:
@Component
@Slf4j
public class EnvRoundRobinRule extends RoundRobinRule {
private AtomicInteger nextServerCyclicCounter;
public EnvRoundRobinRule() {
nextServerCyclicCounter = new AtomicInteger(0);
}
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
log.warn("no load balancer");
return null;
}
Server server = null;
int count = 0;
// 如果失败,重试 10 次
while (Objects.isNull(server) && count++ < 10) {
List<Server> reachableServers = lb.getReachableServers();
List<Server> allServers = lb.getAllServers();
int upCount = reachableServers.size();
int serverCount = allServers.size();
if ((upCount == 0) || (serverCount == 0)) {
log.warn("No up servers available from load balancer: " + lb);
return null;
}
List<NacosServer> filterServers = new ArrayList<>();
String currentEnvironmentVersion = GrayscaleThreadLocalEnvironment.getCurrentEnvironment();
for (Server serverInfo : reachableServers) {
NacosServer nacosServer = (NacosServer) serverInfo;
String version = nacosServer.getMetadata().get("version");
if (version.equals(currentEnvironmentVersion)) {
filterServers.add(nacosServer);
}
}
int filterServerCount = filterServers.size();
int nextServerIndex = incrementAndGetModulo(filterServerCount);
server = filterServers.get(nextServerIndex);
if (server == null) {
Thread.yield();
continue;
}
if (server.isAlive() && (server.isReadyToServe())) {
return (server);
}
server = null;
}
if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: "
+ lb);
}
return server;
}
private int incrementAndGetModulo(int modulo) {
for (; ; ) {
int current = nextServerCyclicCounter.get();
int next = (current + 1) % modulo;
if (nextServerCyclicCounter.compareAndSet(current, next))
return next;
}
}
}
到这 流程基本就已经结束了,下面在header 中增加 userId 为 abc,然后多访问几次,可以看到都被转发到了 灰度环境:
下面在header 中增加 userId 为 110,然后多访问几次,可以看到都被转发到了 生产环境:
边栏推荐
- 深度解读:分布式系统韧性架构压舱石OpenChaos
- Turning "passive" into "active", how to build security compliant intelligent products | Q recommendation
- In depth research and analysis report on global and Chinese oleoresin product market
- Current situation and future development trend of global and Chinese metal casting robot market
- Leetcode 1963. 使字符串平衡的最小交换次数(学习)
- 深度剖析「圈组」关系系统设计 | 「圈组」技术系列文章
- Alibaba Cloud 3 (Soaring Falcon) x86_ 64 (py3.7.8) system Yum source
- CNCF survey in 2021: a year for kubernetes to cross the gap
- System. out. What should I pay attention to when using the println () method
- Nexus of repository manager
猜你喜欢

Distributed file system and enterprise application -- elk enterprise log analysis system

高通WLAN框架学习(29)-- 6GHz 概述

一些经典的嵌入式C面试题汇总

Live800:智能客服提升客户体验的几种方式

. Net C Foundation (6): namespace - scope with name

Ali, tell me about the application scenarios of message oriented middleware?

Webgl programming guide learning (0)

Seven parameters of thread pool and reject policy

Top 10 bone conduction earphones in the list, and five easy-to-use bone conduction earphones are recommended

非常值得学习的调度开源库推荐
随机推荐
Anaconda delete virtual environment
gensim.models word2vec 参数
Alibaba Cloud 3 (Soaring Falcon) x86_ 64 (py3.7.8) system Yum source
2022-2028 global and Chinese near field scanning optical microscope (NSOM) market status and future development trend
[public class preview]: mxplayer Ott audio and video transcoding practice and optimization
为什么需要public static void main(String[ ] args)这个方法?
Turning "passive" into "active", how to build security compliant intelligent products | Q recommendation
Hashicopy之nomad应用编排方案05(访问web页面)
详解 Kubernetes 包管理工具 Helm
MySQL advanced statement
uniapp设置页面跳转效果 - navigateTo切换效果 - 全局animationType动画
In depth research and analysis report on global and Chinese high purity molybdenum market
树莓派知识大扫盲
Current situation and future development trend of global and Chinese transcranial magnetic stimulation coil Market from 2022 to 2028
2022质量员-市政方向-岗位技能(质量员)考试模拟100题及模拟考试
In depth research and analysis report on global and Chinese plant extract products market
【SystemVerilog 之 验证】~ 测试平台、硬件设计描述、激励发生器、监测器、比较器
社交软件Soul撤回IPO申请:上市只差临门一脚 腾讯是大股东
数据库“百亿蓝海”中,每位玩家都能找到一叶扁舟 | C位面对面
老虎国际季报图解:营收5263万美元 持续国际化布局