SpringCloud实现灰度发布的方法步骤
作者:半路出家的小王 发布时间:2023-03-17 05:18:37
1.什么是灰度发布?
灰度发布又称金丝雀发布,是在系统升级的时候能够平滑过渡的一种发布方式。在其上可以进行A/B测试,即让一部分用户继续用产品特性A,一部分用户开始用产品特性B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。
关于金丝雀发布名称的来历:矿工下要矿井,要验证是否有瓦斯,金丝雀对瓦斯很敏感,通过观察金丝雀的反应判断是否安全。
2.灰度发布有什么作用?
1.降低发布带来的影响,虽然功能都在测试环境测过,但毕竟没有发布到生产环境,如果先让少部分用户先使用新版本,提前发现bug,或者性能问题,提前做好修复,就可以降低新版本带来的影响;
2.通过对新老版本的对比,观察新版本带来的效果。结合工作中使用到的灰度发布实践和对其他大厂的灰度发布调研,总结了以下灰度发布方案。
3.灰度发布的实现方式:网关到服务,服务到服务
3.1网关到服务代码实现
3.1.1整体流程
指定灰度规则->预制代码规则->springcloud自定义metadata
3.1.2前置环境(需要自行搭建四个至少服务)
eureka:注册中心
zuul:网关
service-v1:集群服务v1版本
service-v2:集群服务v2版本
3.1.3核心代码
pom.xml
<!-- 实现通过 metadata 进行灰度路由 -->
<dependency>
<groupId>io.jmnarloch</groupId>
<artifactId>ribbon-discovery-filter-spring-cloud-starter</artifactId>
<version>2.1.0</version>
</dependency>
灰度过滤器(核心代码)
@Component
public class GrayFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;//return false 关闭该过滤器
}
@Autowired
private CommonGrayRuleDaoCustom commonGrayRuleDaoCustom;
@Override
public Object run() throws ZuulException {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
String userId = request.getHeader("userId");
// 根据用户id查规则查库,
String rule = findRuleById(userId);
// 金丝雀
if ("v1".equals(rule)) {
RibbonFilterContextHolder.getCurrentContext().add("version", "v1");
// 普通用户
} else if ("v2".equals(rule)) {
RibbonFilterContextHolder.getCurrentContext().add("version", "v2");
}
return null;
}
//查库的伪代码
private String findRuleById(String userId) {
Map<String, String> map = new HashMap();
map.put("9527", "v1");
map.put("9528", "v2");
return map.get(userId);
}
}
3.2网关到服务代码实现
3.2.1整体流程
springcloud自定义metadata->获取当前用户的版本->遍历服务获取服务的的版本,返回合适的服务
3.2.2前置环境(需要自行搭建5个至少服务)
eureka:注册中心
service-A:服务调用方
service-v1:集群服务v1版本
service-v2:集群服务v2版本
3.2.3核心代码
threadlocal工具类
public class RibbonParameters {
private static final ThreadLocal local = new ThreadLocal();
public static <T> T get() {
return (T) local.get();
}
public static <T> void set(T t) {
local.set(t);
}
}
切面获取version的值
@Aspect
@Component
public class RequestAspect {
@Pointcut("execution(* com.mashibing.apipassenger.controller..*Controller*.*(..))")
private void anyMehtod() {
}
@Before(value = "anyMehtod()")
public void before(JoinPoint joinPoint) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String version = request.getHeader("version");
//方式二:
HashMap<Object, Object> map = new HashMap<>();
map.put("version",version);
RibbonParameters.set(map);
}
rule规则
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import com.netflix.niws.loadbalancer.DiscoveryEnabledServer;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.Map;
@Configuration
public class GrayRule extends AbstractLoadBalancerRule {
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
private Server choose(ILoadBalancer lb, Object key) {
System.out.println("灰度,rule");
Server server = null;
while (server == null) {
List<Server> reachableServers = lb.getReachableServers();
//获取当前线程的参数 用户 version=v1
Map<String, String> map = (Map<String, String>) RibbonParameters.get();
String version = "";
if (map != null && map.containsKey("version")) {
version = map.get("version");
}
System.out.println("当前rule,version=" + version);
//遍历服务列表选取用户服务
for (int i = 0; i < reachableServers.size(); i++) {
server = reachableServers.get(i);
//用户的version知道了,服务自定义的meta不知道
Map<String, String> metadata = ((DiscoveryEnabledServer) server).getInstanceInfo().getMetadata();
String metaMap = metadata.get("version");
//用户的version知道了,服务meta也知道了
if (version.trim().equals(metaMap)) {
return server;
}
}
}
return null;
}
}
注意:提前踩坑,No qualifying bean of type ‘com.netflix.loadbalancer.IRule‘ available: expected single matching bean
当是觉得很奇怪,命名自己只定义了grayRule负载均衡策略规则,metadataAwareRule这个我代码中并没有。经过排查自己使用在pom中引入了Ribbon的包,该包默认会带负载均衡策略规则。导致有多个规则,从而报错。
<dependency>
<groupId>io.jmnarloch</groupId>
<artifactId>ribbon-discovery-filter-spring-cloud-starter</artifactId>
<version>2.1.0</version>
</dependency>
删除该包即可
删除后重新运行
服务与服务的灰度发布的另外一种方式:可以在requestAspect中获取到version后,直接比对版本:RibbonFilterContextHolder.getCurrentContext().add("version", "v1"),这种凡是与网关与服务的灰度发布相似。
自此灰度发布完成。
来源:https://blog.csdn.net/f746262041/article/details/124914670


猜你喜欢
- 简介本文我们将会讨怎么在Spring Boot中使用Properties。使用Properties有两种方式,一种是java代码的注解,一种
- 一、什么是组合模式定义:将对象以树形结构组织起来,以达成“部分-整体”的层次结构,使得客户端对单个对象和组合对象的使用具有一致性。动机(Mo
- 1、spring 框架解决字符串编码问题:过滤器 CharacterEncodingFilter(filter-name)2、在web.xm
- 登陆的时候,发现输入账号的不同大小写竟然能够登陆。Mybatis查询代码如下<select id="selectById&q
- 目录匿名方法的代价C#9 中使用静态匿名方法匿名函数 在 C# 中已经出现很多年了,虽然匿名函数用起来很爽,但代价是不小的,为了避免不必要那
- 目录截屏AudioRecord音频采集MediaCodec编码音频数据Rtp发送数据SDP文件配置音频config配置计算方式:vlc测试播
- 什么是环境变量?环境变量通常是指在操作系统当中,用来指定操作系统运行时需要的一些参数。通常为一系列的键值对。path环境变量的作用path环
- 最小堆基本思想:堆对应一棵完全二叉树,且所有非叶结点的值均不大于(或不小于)其子女的值,根结点(堆顶元素)的值是最小(或最大)的,每次都取堆
- android提供的工具链和开发工具比较完善,因此它的开发环境的搭建比较简单,相信许多朋友都已经搭建好环境,并编写了HelloA
- 介绍意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更灵活。主要解决:我们扩展一个类常使用继承方式实现,由于继
- 在textView添加超链接,有两种方式,第一种通过HTML格式化你的网址,一种是设置autolink,让系统自动识别超链接。 代码如下:
- 目录前言一、技术介绍1.ReentranReadWriteLock是什么?二、源码分析1.ReadLock2.WriteLock三、单元测试
- 一、工具类代码public class TaskHelper {#region 多线程操作 &nbs
- 本文实例为大家分享了C#实现简单点餐系统的具体代码,供大家参考,具体内容如下实现简单点餐系统剖析1、设计界面(给菜品的序号(index),名
- java生成json时产生栈溢出错误环境java + hibernate +html本来,java中使用json事件很正常的事,但小心有的地
- annotation就是注解的意思,在我们使用的 * 时,可以通过业务层添加的某个注解,对业务方法进行拦截,之前我们在进行统一方法拦截时使用
- 最近几天一直在看Hadoop相关的书籍,目前稍微有点感觉,自己就仿照着WordCount程序自己编写了一个统计关联商品。需求描述:根据超市的
- 一、什么是代理?指为一个目标对象提供一个代理对象, 并由代理对象控制对目标对象的引用. 使用代理对象, 是为了在不修改目标对象的基础上,增强
- 作为一个初级GIS程序员,关于封装那些宏观的概念暂且不提,编程经常面对的就是“字段,属性,方法”,这也是面向对象的基本概念之一。1.字段通常
- 一、template下文件不允许直接访问1、查资料得知:springboot项目默认是不允许直接访问template下的文件的,是受保护的。