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 灰度发布内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!
原文出处:https://blog.csdn.net/f746262041/article/details/124914670
相关文章
基于springcloud异步线程池、高并发请求feign的解决方案
这篇文章主要介绍了基于springcloud异步线程池、高并发请求feign的解决方案,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-25- 这篇文章主要介绍了Spring AOP 对象内部方法间的嵌套调用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-08-29
Spring Cloud 中@FeignClient注解中的contextId属性详解
这篇文章主要介绍了Spring Cloud 中@FeignClient注解中的contextId属性详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-25Springboot如何实现Web系统License授权认证
这篇文章主要介绍了Springboot如何实现Web系统License授权认证,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-05-28完美解决SpringCloud-OpenFeign使用okhttp替换不生效问题
这篇文章主要介绍了完美解决SpringCloud-OpenFeign使用okhttp替换不生效问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-25如何在Spring WebFlux的任何地方获取Request对象
这篇文章主要介绍了如何在Spring WebFlux的任何地方获取Request对象,帮助大家更好的理解和使用springboot框架,感兴趣的朋友可以了解下...2021-01-26- 这篇文章主要介绍了详解SpringCloudGateway内存泄漏问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-07-16
- @Autowired 注解的主要功能就是完成自动注入,使用也非常简单,但这篇文章主要给大家介绍了关于Spring为什么不推荐使用@Autowired注解的相关资料,需要的朋友可以参考下...2021-11-03
Springboot如何使用mybatis实现拦截SQL分页
这篇文章主要介绍了Springboot使用mybatis实现拦截SQL分页,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-06-19如何解决springcloud feign 首次调用100%失败的问题
这篇文章主要介绍了如何解决springcloud feign 首次调用100%失败的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-06-23- 这篇文章主要介绍了SpringMVC文件上传原理及实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-07-15
Spring Data JPA 关键字Exists的用法说明
这篇文章主要介绍了Spring Data JPA 关键字Exists的用法说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-06-10- 这篇文章主要介绍了spring cloud gateway中如何读取请求参数的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-07-15
tomcat启动完成执行 某个方法 定时任务(Spring)操作
这篇文章主要介绍了tomcat启动完成执行 某个方法 定时任务(Spring)操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-25使用Maven 搭建 Spring MVC 本地部署Tomcat的详细教程
这篇文章主要介绍了使用Maven 搭建 Spring MVC 本地部署Tomcat,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-08-16- 这篇文章主要介绍了Spring Cloud负载均衡及远程调用实现详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2021-09-18
- 这篇文章主要介绍了SpringMvc自动装箱及GET请求参数原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-09-19
- 这篇文章主要介绍了Springboot使用thymeleaf动态模板实现刷新,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-08-31
- 这篇文章主要介绍了SpringMvc获取请求头请求体消息过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-09-17
解决SpringCloud Feign传对象参数调用失败的问题
这篇文章主要介绍了解决SpringCloud Feign传对象参数调用失败的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-06-24