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

 更新时间:2022年05月26日 16:03:42   作者:半路出家的小王  
本文主要介绍了SpringCloud实现灰度发布的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

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"),这种凡是与网关与服务的灰度发布相似。

自此灰度发布完成。

到此这篇关于SpringCloud实现灰度发布的方法步骤的文章就介绍到这了,更多相关SpringCloud 灰度发布内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring中配置数据源的几种方式

    Spring中配置数据源的几种方式

    今天小编就为大家分享一篇关于Spring中配置数据源的几种方式,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • Spring Boot 指定外部启动配置文件详解

    Spring Boot 指定外部启动配置文件详解

    在springboot项目中,也可以使用yml类型的配置文件代替properties文件。接下来通过本文给大家分享Springboot配置文件的使用,感兴趣的朋友一起看看吧
    2021-09-09
  • Springboot Autowried及Resouce使用对比解析

    Springboot Autowried及Resouce使用对比解析

    这篇文章主要介绍了Springboot Autowried及Resouce使用对比解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • java HashMap 的工作原理详解

    java HashMap 的工作原理详解

    本文主要介绍java HashMap 的资料,这里整理了相关资料,并详细说明了HashMap的用法,有需要的小伙伴可以参考下
    2016-09-09
  • 5个步骤让你明白多线程和线程安全

    5个步骤让你明白多线程和线程安全

    本文详细讲解了多线程和线程安全的实现,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-12-12
  • Spring Cloud Hystrix异常处理方法详解

    Spring Cloud Hystrix异常处理方法详解

    这篇文章主要介绍了Spring Cloud Hystrix异常处理方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-01-01
  • java中的编码转换过程(以utf8和gbk为例)

    java中的编码转换过程(以utf8和gbk为例)

    这篇文章主要介绍了java中的编码转换过程(以utf8和gbk为例),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • Java jvm中Code Cache案例详解

    Java jvm中Code Cache案例详解

    这篇文章主要介绍了Java jvm中Code Cache案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • 详解Spring框架下向异步线程传递HttpServletRequest参数的坑

    详解Spring框架下向异步线程传递HttpServletRequest参数的坑

    这篇文章主要介绍了详解Spring框架下向异步线程传递HttpServletRequest参数的坑,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-03-03
  • SpringBoot在一定时间内限制接口请求次数的实现示例

    SpringBoot在一定时间内限制接口请求次数的实现示例

    在项目中,接口的暴露在外面,很多人就会恶意多次快速请求,本文主要介绍了SpringBoot在一定时间内限制接口请求次数的实现示例,具有一定的参考价值,感兴趣的可以了解一下
    2022-03-03

最新评论