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 &lsquo;com.netflix.loadbalancer.IRule&lsquo; available: expected single matching bean

SpringCloud实现灰度发布的方法步骤

当是觉得很奇怪,命名自己只定义了grayRule负载均衡策略规则,metadataAwareRule这个我代码中并没有。经过排查自己使用在pom中引入了Ribbon的包,该包默认会带负载均衡策略规则。导致有多个规则,从而报错。

<dependency>
   <groupId>io.jmnarloch</groupId>
   <artifactId>ribbon-discovery-filter-spring-cloud-starter</artifactId>
   <version>2.1.0</version>
</dependency>

删除该包即可

删除后重新运行

SpringCloud实现灰度发布的方法步骤

服务与服务的灰度发布的另外一种方式:可以在requestAspect中获取到version后,直接比对版本:RibbonFilterContextHolder.getCurrentContext().add("version", "v1"),这种凡是与网关与服务的灰度发布相似。

自此灰度发布完成。

来源:https://blog.csdn.net/f746262041/article/details/124914670

标签:Spring,Cloud,灰度发布
0
投稿

猜你喜欢

  • C语言数据的存储专项分析

    2022-05-05 13:35:24
  • Android实现声音采集回声与回声消除

    2022-09-28 12:13:17
  • Android 图片处理缩放功能

    2023-09-29 13:59:54
  • Android自定义View制作仪表盘界面

    2021-10-05 06:45:01
  • java基于UDP实现在线聊天功能

    2021-06-08 00:01:44
  • Java中对象的深复制(深克隆)和浅复制(浅克隆)介绍

    2023-02-20 12:05:39
  • android命令行模拟输入事件(文字、按键、触摸等)

    2021-10-30 04:48:06
  • Android Socket实现多个客户端聊天布局

    2022-10-09 08:31:19
  • Java synchronized关键_动力节点Java学院整理

    2023-11-10 11:08:53
  • volatile与happens-before的关系与内存一致性错误

    2021-12-13 20:25:37
  • Spring MVC请求参数的深入解析

    2021-11-26 22:55:25
  • SpringBoot整合Docker实现一次构建到处运行的操作方法

    2023-01-23 04:32:14
  • java简单手写版本实现时间轮算法

    2023-01-05 14:50:38
  • 用Android studio实现简易计算器功能

    2021-07-31 21:06:13
  • Spring Boot项目中定制拦截器的方法详解

    2021-07-28 19:21:55
  • Springboot框架实现自动装配详解

    2023-03-03 21:11:14
  • 详解二维码生成工厂

    2022-09-26 11:26:13
  • Unity3D 计时器的实现代码(三种写法总结)

    2022-02-10 08:51:08
  • Java多线程编程综合案例详解

    2023-12-09 18:13:25
  • Android App使用RecyclerView实现上拉和下拉刷新的方法

    2023-03-28 17:47:20
  • asp之家 软件编程 m.aspxhome.com