基于springcloud实现的灰度发布

标签: | 发表时间:2020-02-16 21:43 | 作者:
出处:https://github.com

其他参考:


springcloud-gray

基于springcloud实现的灰度发布

架构设计和界面

架构模型

灰度发布架构设计图 灰度发布数据流图

平台化操作

平台化界面图

项目结构

gray-config-server 配置中心

端口:6007,方便起见直接读取配置文件,生产环境可以读取git。先启动配置中心,所有服务的配置(包括注册中心的地址)均从配置中心读取。

gray-xxx-service 服务消费者

调用服务提供者和服务提供者,验证是否进入灰度服务。

gray-core 框架核心包

核心jar包,所有微服务均引用该包,用于负载自定义策略规则,是实现灰度发布的核心架包。

gray-service-registry-center 注册中心

端口:6006,用于统筹各个注册服务。

gray-api-gateway 网关

端口:4002,拉取灰度策略,进行请求的把标签操作。

应用场景

灰度发布

通过灰度版本的控制,实现符合灰度策略的对象,优先进入灰度服务进行体验。

异构服务的共存

例如,根据不同的策略,有根据不同的渠道、地域、门店、品牌等,优先使用不同的服务。例如,广州地域的用户,仅能使用基于广州部署的微服务。

同等级服务的调用

例如,业务场景,根据不同的渠道和来源进行下单。微信的下单,仅能调用微信的order-service服务;官网下单,仅能调用官网的order--service下单; 通过这样的方式,上层业务无须调用何种具体服务统一底层进行负载调用,实现业务的解耦和服务的可插拔配置;

实现思路

根据标签的控制,我们当然放到之前写的Ribbon的** CustomMetadataRule 中,每个实例配置的不同规则也是跟之前一样放到注册中心的metadata中,关键是标签数据如何传过来。自定义规则[ CustomMetadataRule ]的实现思路里面有答案,请求都通过 gray-api-gateway 进来,因此我们可以在zuul里面给请求打标签,基于用户,IP或其他看你的需求,然后将标签信息放入Thystrix。hystrix的原理,为了做到故障隔离,hystrix启用了自己的线程。另外使用sleuth方案,他的链路跟踪就能够将spam传递下去,翻翻sleuth源码,找找其他资料,发现可以使用 HystrixRequestVariableDefault ,这里不建议直接使用 HystrixConcurrencyStrategy ,会和sleuth的strategy冲突。代码参见 CoreHeaderInterceptor**。现在可以测试zuul里面的rule,看能否拿到标签内容了。

这里还不是终点,解决了zuul的路由,服务A调服务B这里的路由怎么处理呢?zuul算出来的标签如何往后面依次传递下去呢,我们还是抄sleuth:把标签放入header,服务A调服务B时,将服务A header里面的标签放到服务B的header里,依次传递下去。这里的关键点就是:内部的微服务在接收到发来的请求时(gateway-->A,A-->B都是这种情况)。

总结一下:zuul依据用户或IP等计算标签,并将标签放入header里向后传递,后续的微服务通过拦截器,将header里的标签放入RestTemplate请求的header里继续向后接力传递。将灰度标识放入( HystrixRequestVariableDefault),使Ribbon Rule可以使用。

代码分析实现流程

自定义的规则,该处可以实现针对不同的策略,使用不同的负载机制[ 轮询、随机、权重随机]

publicclassCustomMetadataRuleextendsZoneAvoidanceRule{//检测灰度开关是否启动privateHttpResultcheckGraySwitch() {Stringurl="http://10.200.102.136:6015/eureka/apps/switch";HttpResultresult=newHttpResult();
		result.statusCode=500;try{
			result=HttpClient.get(url,null);
		}catch(Exceptione1) {
			e1.printStackTrace();
		}returnresult;
	}@OverridepublicServerchoose(Objectkey) {//获取是否存在存活的服务可调用List<Server>serverList=this.getPredicate().getEligibleServers(this.getLoadBalancer().getAllServers());//获取不到服务if(CollectionUtils.isEmpty(serverList)) {returnnull;
		}//获取灰度开关是否启动HttpResultresult=checkGraySwitch();//灰度开关被设置成关闭状态,默认走空metadata或者是特定标识是正常的服务,轮询访问BooleanisOpen=Boolean.parseBoolean(JSONObject.parseObject(result.content).getString("errorMsg"));if(result.statusCode==200&&!isOpen) {
			isOpen=true;returnRoundRobinRuleBySelf.getInstance().choose(this.getLoadBalancer(), key,isOpen);
		}//灰度发布启动状态,未被设置成灰度对象,默认走空metadata或者是特定标识是正常的服务,轮询访问if(StringUtils.isEmpty(CoreHeaderInterceptor.label.get())) {
			isOpen=false;returnRoundRobinRuleBySelf.getInstance().choose(this.getLoadBalancer(), key,isOpen);
		}//灰度发布启动状态,被设置成灰度对象,走空特定标识的服务,轮询访问returnRoundRobinRuleBySelf.getInstance().choose(this.getLoadBalancer(), key,!isOpen);
	}
}

feignClient 调用flag位透传的问题

publicclassCoreFeignRequestInterceptorimplementsRequestInterceptor{privatestaticfinalLoggerlogger=LoggerFactory.getLogger(CoreHttpRequestInterceptor.class);@Overridepublicvoidapply(RequestTemplatetemplate) {Stringheader=StringUtils.collectionToDelimitedString(CoreHeaderInterceptor.label.get(),CoreHeaderInterceptor.HEADER_LABEL_SPLIT);Stringtag=CoreHeaderInterceptor.tag.get();
		template.header(CoreHeaderInterceptor.HEADER_LABEL, header).header(CoreHeaderInterceptor.HEADER_TAG, tag);
		logger.info("label:"+header+"tag :"+tag);
	}

}

HttpRequest 调用flag位透传的问题

publicclassCoreHttpRequestInterceptorimplementsClientHttpRequestInterceptor{privatestaticfinalLoggerlogger=LoggerFactory.getLogger(CoreHttpRequestInterceptor.class);@OverridepublicClientHttpResponseintercept(HttpRequestrequest,byte[]body,ClientHttpRequestExecutionexecution)throwsIOException{HttpRequestWrapperrequestWrapper=newHttpRequestWrapper(request);Stringheader=StringUtils.collectionToDelimitedString(CoreHeaderInterceptor.label.get(),CoreHeaderInterceptor.HEADER_LABEL_SPLIT);Stringtag=CoreHeaderInterceptor.tag.get();
        
        logger.info("label:"+header+"tag :"+tag);HttpHeadersheaders=requestWrapper.getHeaders();
        headers.add(CoreHeaderInterceptor.HEADER_LABEL, header);
        headers.add(CoreHeaderInterceptor.HEADER_TAG, tag);returnexecution.execute(requestWrapper, body);
    }
}

配置生效

@Configu[email protected]ConfigurerAdapter{@BeanpublicDefaultPropertiesFactorydefaultPropertiesFactory() {returnnewDefaultPropertiesFactory();
	}@[email protected]() {RestTemplaterestTemplate=newRestTemplate();
		restTemplate.getInterceptors().add(newCoreHttpRequestInterceptor());returnrestTemplate;
	}//用于配置feignClient透传生效@BeanpublicFeign.BuilderfeignBuilder() {returnFeign.builder().requestInterceptor(newCoreFeignRequestInterceptor());
	}@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry) {
		registry.addInterceptor(newCoreHeaderInterceptor());
	}
}

测试

测试/验证流程说明:

第一步,测试灰度发布总开关是否生效

简易形式:

1.先启动 order服务;

1.1 未标识灰度服务时,前端每一次访问都是随机的情况 访问url: http://127.0.0.1:4002/order/inner/order/getOrderInfoListByUserName?userName=liulianyuan 图一1.2 分别启动灰度服务和正常服务。一开始启动的时候是无差别的。我们可以在 metadata.html 进行动态配置,指定某个服务是灰度的。 以下,设置了order-service2 是灰度服务。 访问url: http://127.0.0.1:4002/order/inner/order/getOrderInfoListByUserName?userName=liulianyuan 图二 图二 图二 图二

第二步,测试灰度发布的正常行为

方式1------(灰度用户): 前端 ==> 网关 ==> 正常服务 ==> 灰度服务

1.启动 order服务 和 user服务;

1.1 user-service是正常服务,标识user-service2为灰度服务;order-service 均是正常服务。 1.2 启动所有服务。一开始启动的时候是无差别的。我们可以在 metadata.html 进行动态配置,指定某个服务是灰度的。

请求url: http://127.0.0.1:4002/order/test?userName=liulianyuan 图二 图二该处是随机访问正常的order-service服务的。多试几次,就可以看见order-service和order-service2的出现。【该处如果需要做成轮需,需要改代码】

方式2------(灰度用户): 前端 ==> 网关 ==> 灰度服务 ==> 正常服务

1.启动 order服务 和 user服务;

1.1 user-service是正常服务,标识user-service2为灰度服务;order-service 均是正常服务。 1.2 启动所有服务。一开始启动的时候是无差别的。我们可以在 metadata.html 进行动态配置,指定某个服务是灰度的。 图二请求url: http://127.0.0.1:4002/user/getOrderInfo?userName=liulianyuan 图二 图二该处是随机访问正常的order-service服务的。多试几次,就可以看见order-service和order-service2的出现。【该处如果需要做成轮需,需要改代码】

方式3------(灰度用户): 前端 ==> 网关 ==> 灰度服务 ==> 灰度服务

1.启动 order服务 和 user服务;

1.1 user-service是正常服务,标识user-service2为灰度服务;order-service 均是正常服务。 1.2 启动所有服务。一开始启动的时候是无差别的。我们可以在 metadata.html 进行动态配置,指定某个服务是灰度的。 图二请求url: http://127.0.0.1:4002/order/test?userName=liulianyuan 图二

方式4------(正常用户): 前端 ==> 网关 ==> 正常服务 ==> 正常服务

1.启动 order服务 和 user服务;

1.1 user-service是正常服务,标识user-service2为灰度服务;order-service 均是正常服务。 1.2 启动所有服务。一开始启动的时候是无差别的。我们可以在 metadata.html 进行动态配置,指定某个服务是灰度的。 图二请求url: http://127.0.0.1:4002/order/test?userName=lly 图二

相关 [springcloud 灰度] 推荐:

基于springcloud实现的灰度发布

- -
基于springcloud实现的灰度发布. gray-config-server 配置中心. 端口:6007,方便起见直接读取配置文件,生产环境可以读取git. 先启动配置中心,所有服务的配置(包括注册中心的地址)均从配置中心读取. gray-xxx-service 服务消费者. 调用服务提供者和服务提供者,验证是否进入灰度服务.

SpringCloud灰度发布实践(附源码) - 微服务实践 - SegmentFault 思否

- -
在平时的业务开发过程中,后端服务与服务之间的调用往往通过. resttemplate两种方式. 但是我们在调用服务的时候往往只需要写服务名就可以做到路由到具体的服务,这其中的原理相比大家都知道是. ribbon组件帮我们做了负载均衡的功能. 灰度的核心就是路由,如果我们能够重写ribbon默认的负载均衡算法是不是就意味着我们能够控制服务的转发呢.

SpringCloud项目接入Jaeger(下) - 掘金

- -
spring-cloud-sleuth这个组件时,会面临两个问题. 首先是日志中无法显示traceId和spanId这些链路信息,其次是不能在用. spring-cloud-sleuth所提供的方式进行链路传值. spring-cloud-sleuth是将traceId等链路信息保存在. slf4j的MDC(Mapped Diagnostic Contexts)中,然后通过%X{traceId}这种方式将traceId提取出来,比如打印到控制台的默认格式是:.

Springcloud + RocketMQ 解决分布式事务

- - 掘金架构
分布式事务有哪些实现方式. 随着互联网时代的高速发展,分布式成了大型系统的标配,这是时代发展的选择. 大型分布式系统不是每个公司和开发人员都能够涉及的领域,因为大型系统后面都 隐藏着众多代名词:复杂,昂贵,高科技,人才云集,大战略. 大部分领头互联网公司甚至依托自己的分布式经验逐步建立自己的体系,并使用这套体系搭建自己的平台对内,甚至对外提供服务, 就像现在众多的云平台提供的服务,甚至有些大战略提出促进发展:大中台小前台、大炮台支援单兵作战等等.

SpringCloud基础教程(五)-配置中心热生效和高可用

- - 掘金后端
 我的博客: 兰陵笑笑生,欢迎浏览博客.  上一章 SpringCloud基础教程(四)-配置中心入门当中,我们在对Eureka的有了基本的基础认识之上,深入的了解Eureka高可用集群和其他的生产环境中用到的一些配置. 本章将开始了解分布式环境下的配置中心.  在实际的项目运行中,我们会根据实际需求修改配置内容,那么有没有一种方式,能够在不启动服务组件的情况向让配置文件动态的生效呢,Spring Cloud Conifg中提供了一种方式了.

微服务SpringCloud之GateWay熔断、限流、重试 - 社会主义接班人 - 博客园

- -
纯洁的微笑的Spring Cloud系列博客终于学完了,也对Spring Cloud有了初步的了解. StripPrefix Filter 是一个请求路径截取的功能,我们可以利用这个功能来做特殊业务的转发. StripPrefix是当请求路径匹配到/name/**会将包含name和后边的字符串接去掉转发, StripPrefix=2就代表截取路径的个数,当访问.

Nginx+KV db进行AB灰度测试

- - IT技术博客大学习
周6参加华东运维大会,听了人家淘宝用nginx的一些场景,其中AB的灰度测试可能适用场景会比较普遍,当然大会上,并没有详细讨论实现. 大概需求是: 网站类业务在更新new feature时,并不想让全量用户看到,可以针对地区性用户开放此feature. 大概构思了一个方式,使用 nginx+redis/memcache+IP库实现,简单的流程图如下:.

nginx+lua+memcache实现灰度发布

- - 开源软件 - ITeye博客
灰度发布在百度百科中解释:. 灰度发布是指在黑与白之间,能够平滑过渡的一种发布方式. AB test就是一种灰度发布方式,让一部分用户继续用A,一部分用户开始用B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面 来. 灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度.

中国互联网走在“灰度”上

- - 互联网 - ITeye博客
中国互联网走在“灰度”上(转). 第一次听说灰度这个词,是任正非说新型管理者所需要的素质. 似乎其他人包括马云也用不同的语言说过类似的意思. 灰度这个词所包含的意义和视野是广远的. 要理解这个词,可能同样要用“灰度”的心态. 灰度的反面,是规规矩矩,清清楚楚,泾渭分明,严谨条理,是决不妥协,不转弯,认死理.

灰度发布系统的实现

- - codedump
灰度发布,已经不是一个很新的概念了.一个产品,如果需要快速迭代开发上线,又要保证质量,保证刚上线的系统,一旦出现问题那么可以很快的控制影响面,就需要设计一套灰度发布系统.. 灰度发布系统的作用在于,可以根据自己的配置,来将用户的流量导到新上线的系统上,来快速验证新的功能修改,而一旦出问题,也可以马上的恢复,简单的说,就是一套A/BTest系统..