• 灰度发布
  • 微服务全链路灰度
  • 全链路灰度设计思路
    • 标签路由
    • 节点打标
    • 流量染色
    • 分布式链路追踪 ThreadLocal
    • 流量治理平台 Nacos 配置中心
  • 全链路灰度实现
  • Discovery
  • 使用
    • 一、父pom引入Discovery
    • 二、Gateway 引入 — 网关 discovery-plugin-strategy-starter-gateway
    • 三、微服务 引入 — 网关 discovery-plugin-strategy-starter-service
    • 四、Gateway 配置 权重、流量百分比、Header参数
  • 原理分析
    • Openfeign 通过 RequestInterceptor
    • Gateway 通过 GlobalFilter
    • 通过IRule
    • 通过 pluginAdapter 可以拿到需要的数据
  • MSE 微服务治理全链路灰度 太贵

灰度发布

不停机旧版本,部署新版 本,高比例流量(例如:95%)走旧版本,低比例流量(例如:5%)切换到新版本,通过 监控观察无问题,逐步扩大范围,最终把所有流量都迁移到新版本上。属无损发布。

  • 优点
    灵活简单,不需要用户标记驱动。
    安全性高,新版本如果出现问题,只会发生在低比例的流 量上
  • 缺点
    成本较高,需要部署稳定/灰度两套环境

微服务全链路灰度

在发布过程中,我们只需部署服务的灰度版本,流量在调用链路上流转 时,由流经的网关、各个中间件以及各个微服务来识别灰度流量,并动态转发至对应服务的 灰度版本

无论是微服务网关还是微服务本身都需要识别流量,根据治理规则做出动态决策。当服务 版本发生变化时,这个调用链路的转发也会实时改变

全链路灰度设计思路

1. 标签路由

通过对服务下所有节点按照标签名和标签值不同进行分组,使得订阅该服务节点信 息的服务消费端可以按需访问该服务的某个分组

2. 节点打标

  • k8s – labels.version=gray
    在使用Kubernetes Service作为服务发现的业务系统中,服务提供者通过向ApiServer提交 Service资源完成服务暴露,服务消费端监听与该Service资源下关联的Endpoint资源,从 Endpoint资源中获取关联的业务Pod 资源,读取上面的Labels数据并作为该节点的元数据 信息。所以,我们只要在业务应用描述资源Deployment中的Pod模板中为节点添加标签即可。

  • nacos – `spring.cloud.nacos.discovery.metadata.version=gray

spring:cloud:discovery:server-addr: nacos.localhost.com:8848metadata:version: 1.1

3. 流量染色

  • 可以在请求的源头上对 流量进行染色
  • 可以在微服务网关上对匹配特定路由规则的请求动态添加流量标识 (Gateway GlobalFilter)
  • 流量在链路中流经灰度节点时,如果请求信息中不含有灰度标识,需要自动为其染色 (Openfeign RequestInterceptor)

4. 分布式链路追踪 ThreadLocal

  • 借助于分布式链路追踪思想,我们也可以传递一些自定义信息,比如灰度标识

5. 流量治理平台 Nacos 配置中心

  • 需要引入一个中心化的流量治理平台, 方便各个业务线的开发者定义自己的全链路灰度规则

全链路灰度实现

Discovery

https://github.com/Nepxion/Discovery
https://github.com/Nepxion/Discovery/wiki
http://polaris-paas.gitee.io/polaris-sdk/#/
http://nepxion.gitee.io/discovery/#/?id=入门主页

使用

一、父pom引入Discovery

<spring-cloud.version>Hoxton.SR12</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.8.RELEASE</spring-cloud-alibaba.version>
<discovery.version>6.20.0-SNAPSHOT</discovery.version>
...<dependency><groupId>com.nepxion</groupId><artifactId>discovery</artifactId><version>${discovery.version}</version><type>pom</type><scope>import</scope></dependency>

二、Gateway 引入 — 网关 discovery-plugin-strategy-starter-gateway

pom.xml

<!-- 1.注册中心插件 -->
<dependency><groupId>com.nepxion</groupId><artifactId>discovery-plugin-register-center-starter-nacos</artifactId>
</dependency><!-- 2.配置中心插件 -->
<dependency><groupId>com.nepxion</groupId><artifactId>discovery-plugin-config-center-starter-nacos</artifactId>
</dependency><!-- 3.管理中心插件 -->
<dependency><groupId>com.nepxion</groupId><artifactId>discovery-plugin-admin-center-starter</artifactId>
</dependency><!-- 4.网关策略编排插件 -->
<dependency><groupId>com.nepxion</groupId><artifactId>discovery-plugin-strategy-starter-gateway</artifactId>
</dependency>

Gateway 配置

spring:application:name: demomall-gatewaystrategy:gateway:dynamic:route:enabled: true #开启网关订阅配置中心的动态路由策略,默认为falsecloud:discovery:metadata:group: discovery-group #组名必须配置

根据实际的灰度发布维度和场景,配置染色方式的元数据

#组名必须要配置,版本、区域、环境和可用区根据具体场景选择其中一种或者几种进行配置
spring.cloud.discovery.metadata.group=discovery-guide-group
spring.cloud.discovery.metadata.version=1.0
spring.cloud.discovery.metadata.region=dev
spring.cloud.discovery.metadata.env=env1
spring.cloud.discovery.metadata.zone=zone1
spring.cloud.discovery.metadata.active=true

三、微服务 引入 — 网关 discovery-plugin-strategy-starter-service

pom.xml

<!--discovery 1.注册中心插件 -->
<dependency><groupId>com.nepxion</groupId><artifactId>discovery-plugin-register-center-starter-nacos</artifactId>
</dependency><!--discovery 2.配置中心插件 -->
<dependency><groupId>com.nepxion</groupId><artifactId>discovery-plugin-config-center-starter-nacos</artifactId>
</dependency><!--discovery 3.管理中心插件 -->
<dependency><groupId>com.nepxion</groupId><artifactId>discovery-plugin-admin-center-starter</artifactId>
</dependency><!--discovery 4.网关策略编排插件 -->
<dependency><groupId>com.nepxion</groupId><artifactId>discovery-plugin-strategy-starter-service</artifactId>
</dependency>

配置

spring:application:name: demomall-membercloud:discovery: #discovery配置,设置流量染色的元数据metadata:group: discovery-group #组名必须配置version: 1.0 #指定版本号

再新启一个配置VM

-Dserver.port=8878
-Dspring.cloud.discovery.metadata.version=1.1
-javaagent:/root/skywalking/skywalking-agent/skywalking-agent.jar
-DSW_AGENT_NAME=demomall-member
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800

四、Gateway 配置

1. 全链路版本权重灰度发布

在nacos配置中心中增加网关的版本权重灰度发布策略

  • Group为discovery-group
  • Data Id为demomall-gateway
  • 策略内容如下,实现从网关发起的调用全链路1.0版本流量权重 为90%,1.1版本流量权重为10%
<?xml version="1.0" encoding="UTF-8"?>
<rule><strategy><version-weight>1.0=90;1.1=10</version-weight></strategy>
</rule><?xml version="1.0" encoding="UTF-8"?>
<rule><strategy><version-weight>{"discovery-first":"1.0=90;1.1=10", "discovery-second":"1.0=90;1.1=10"}</version-weight></strategy>
</rule>

2. 全链路版本条件权重灰度发布 – 指定百分比流量分配

  • 灰度路由,即服务a和b 1.1版本被调用到的概率为5%
  • 稳定路由,即服务a和b 1.0版本被调用到的概率为95%
<?xml version="1.0" encoding="UTF-8"?>
<rule><strategy-release><conditions type="gray"><condition id="gray-condition" version-id="gray-route=5;stable-route=95"/></conditions><routes><route id="gray-route" type="version">{"discovery-first":"1.1", "discovery-second":"1.1"}</route><route id="stable-route" type="version">{"discovery-first":"1.0", "discovery-second":"1.0"}</route></routes></strategy-release>
</rule>

3. 根据前端传递的Header参数动态选择百分比流量分配

<?xml version="1.0" encoding="UTF-8"?>
<rule><strategy-release><conditions type="gray"><!-- 灰度路由1,条件expression驱动 --><condition id="gray-condition-1" expression="#H['a'] == '1'" version-id="gray-route=10;stable-route=90"/><!-- 灰度路由2,条件expression驱动 --><condition id="gray-condition-2" expression="#H['a'] == '1' and #H['b'] == '2'" version-id="gray-route=85;stable-route=15"/><!-- 兜底路由,无条件expression驱动 --><condition id="basic-condition" version-id="gray-route=0;stable-route=100"/></conditions><routes><route id="gray-route" type="version">{"discovery-first":"1.1", "discovery-second":"1.1"}</route><route id="stable-route" type="version">{"discovery-first":"1.0", "discovery-second":"1.0"}</route></routes></strategy-release>
</rule>

原理分析

Openfeign 通过 RequestInterceptor

Openfeign RequestInterceptor

public interface RequestInterceptor {void apply(RequestTemplate template);
}

com.nepxion.discovery.plugin.strategy.aop.FeignStrategyInterceptor

public class FeignStrategyInterceptor extends AbstractStrategyInterceptor implements RequestInterceptor {@Autowiredprotected StrategyContextHolder strategyContextHolder;@Value("${spring.application.strategy.feign.core.header.transmission.enabled:true}")protected Boolean feignCoreHeaderTransmissionEnabled;public FeignStrategyInterceptor(String contextRequestHeaders, String businessRequestHeaders) {super(contextRequestHeaders, businessRequestHeaders);}public void apply(RequestTemplate requestTemplate) {this.interceptInputHeader();this.applyInnerHeader(requestTemplate);this.applyOuterHeader(requestTemplate);this.interceptOutputHeader(requestTemplate);}private void applyInnerHeader(RequestTemplate requestTemplate) {requestTemplate.header("n-d-service-group", new String[]{this.pluginAdapter.getGroup()});requestTemplate.header("n-d-service-type", new String[]{this.pluginAdapter.getServiceType()});String serviceAppId = this.pluginAdapter.getServiceAppId();if (StringUtils.isNotEmpty(serviceAppId)) {requestTemplate.header("n-d-service-app-id", new String[]{serviceAppId});}requestTemplate.header("n-d-service-id", new String[]{this.pluginAdapter.getServiceId()});requestTemplate.header("n-d-service-address", new String[]{this.pluginAdapter.getHost() + ":" + this.pluginAdapter.getPort()});String version = this.pluginAdapter.getVersion();if (StringUtils.isNotEmpty(version) && !StringUtils.equals(version, "default")) {requestTemplate.header("n-d-service-version", new String[]{version});}String region = this.pluginAdapter.getRegion();if (StringUtils.isNotEmpty(region) && !StringUtils.equals(region, "default")) {requestTemplate.header("n-d-service-region", new String[]{region});}String environment = this.pluginAdapter.getEnvironment();if (StringUtils.isNotEmpty(environment) && !StringUtils.equals(environment, "default")) {requestTemplate.header("n-d-service-env", new String[]{environment});}String zone = this.pluginAdapter.getZone();if (StringUtils.isNotEmpty(zone) && !StringUtils.equals(zone, "default")) {requestTemplate.header("n-d-service-zone", new String[]{zone});}}private void applyOuterHeader(RequestTemplate requestTemplate) {Enumeration<String> headerNames = this.strategyContextHolder.getHeaderNames();String routeAddressBlacklist;if (headerNames != null) {while(headerNames.hasMoreElements()) {String headerName = (String)headerNames.nextElement();routeAddressBlacklist = this.strategyContextHolder.getHeader(headerName);boolean isHeaderContains = this.isHeaderContainsExcludeInner(headerName.toLowerCase());if (isHeaderContains) {if (this.feignCoreHeaderTransmissionEnabled) {requestTemplate.header(headerName, new String[]{routeAddressBlacklist});} else {boolean isCoreHeaderContains = StrategyUtil.isCoreHeaderContains(headerName);if (!isCoreHeaderContains) {requestTemplate.header(headerName, new String[]{routeAddressBlacklist});}}}}}if (this.feignCoreHeaderTransmissionEnabled) {Map<String, Collection<String>> headers = requestTemplate.headers();if (CollectionUtils.isEmpty((Collection)headers.get("n-d-version"))) {routeAddressBlacklist = this.strategyContextHolder.getRouteVersion();if (StringUtils.isNotEmpty(routeAddressBlacklist)) {requestTemplate.header("n-d-version", new String[]{routeAddressBlacklist});}}if (CollectionUtils.isEmpty((Collection)headers.get("n-d-region"))) {routeAddressBlacklist = this.strategyContextHolder.getRouteRegion();if (StringUtils.isNotEmpty(routeAddressBlacklist)) {requestTemplate.header("n-d-region", new String[]{routeAddressBlacklist});}}if (CollectionUtils.isEmpty((Collection)headers.get("n-d-env"))) {routeAddressBlacklist = this.strategyContextHolder.getRouteEnvironment();if (StringUtils.isNotEmpty(routeAddressBlacklist)) {requestTemplate.header("n-d-env", new String[]{routeAddressBlacklist});}}if (CollectionUtils.isEmpty((Collection)headers.get("n-d-address"))) {routeAddressBlacklist = this.strategyContextHolder.getRouteAddress();if (StringUtils.isNotEmpty(routeAddressBlacklist)) {requestTemplate.header("n-d-address", new String[]{routeAddressBlacklist});}}if (CollectionUtils.isEmpty((Collection)headers.get("n-d-version-weight"))) {routeAddressBlacklist = this.strategyContextHolder.getRouteVersionWeight();if (StringUtils.isNotEmpty(routeAddressBlacklist)) {requestTemplate.header("n-d-version-weight", new String[]{routeAddressBlacklist});}}if (CollectionUtils.isEmpty((Collection)headers.get("n-d-region-weight"))) {routeAddressBlacklist = this.strategyContextHolder.getRouteRegionWeight();if (StringUtils.isNotEmpty(routeAddressBlacklist)) {requestTemplate.header("n-d-region-weight", new String[]{routeAddressBlacklist});}}if (CollectionUtils.isEmpty((Collection)headers.get("n-d-version-prefer"))) {routeAddressBlacklist = this.strategyContextHolder.getRouteVersionPrefer();if (StringUtils.isNotEmpty(routeAddressBlacklist)) {requestTemplate.header("n-d-version-prefer", new String[]{routeAddressBlacklist});}}if (CollectionUtils.isEmpty((Collection)headers.get("n-d-version-failover"))) {routeAddressBlacklist = this.strategyContextHolder.getRouteVersionFailover();if (StringUtils.isNotEmpty(routeAddressBlacklist)) {requestTemplate.header("n-d-version-failover", new String[]{routeAddressBlacklist});}}if (CollectionUtils.isEmpty((Collection)headers.get("n-d-region-transfer"))) {routeAddressBlacklist = this.strategyContextHolder.getRouteRegionTransfer();if (StringUtils.isNotEmpty(routeAddressBlacklist)) {requestTemplate.header("n-d-region-transfer", new String[]{routeAddressBlacklist});}}if (CollectionUtils.isEmpty((Collection)headers.get("n-d-region-failover"))) {routeAddressBlacklist = this.strategyContextHolder.getRouteRegionFailover();if (StringUtils.isNotEmpty(routeAddressBlacklist)) {requestTemplate.header("n-d-region-failover", new String[]{routeAddressBlacklist});}}if (CollectionUtils.isEmpty((Collection)headers.get("n-d-env-failover"))) {routeAddressBlacklist = this.strategyContextHolder.getRouteEnvironmentFailover();if (StringUtils.isNotEmpty(routeAddressBlacklist)) {requestTemplate.header("n-d-env-failover", new String[]{routeAddressBlacklist});}}if (CollectionUtils.isEmpty((Collection)headers.get("n-d-zone-failover"))) {routeAddressBlacklist = this.strategyContextHolder.getRouteZoneFailover();if (StringUtils.isNotEmpty(routeAddressBlacklist)) {requestTemplate.header("n-d-zone-failover", new String[]{routeAddressBlacklist});}}if (CollectionUtils.isEmpty((Collection)headers.get("n-d-address-failover"))) {routeAddressBlacklist = this.strategyContextHolder.getRouteAddressFailover();if (StringUtils.isNotEmpty(routeAddressBlacklist)) {requestTemplate.header("n-d-address-failover", new String[]{routeAddressBlacklist});}}if (CollectionUtils.isEmpty((Collection)headers.get("n-d-id-blacklist"))) {routeAddressBlacklist = this.strategyContextHolder.getRouteIdBlacklist();if (StringUtils.isNotEmpty(routeAddressBlacklist)) {requestTemplate.header("n-d-id-blacklist", new String[]{routeAddressBlacklist});}}if (CollectionUtils.isEmpty((Collection)headers.get("n-d-address-blacklist"))) {routeAddressBlacklist = this.strategyContextHolder.getRouteAddressBlacklist();if (StringUtils.isNotEmpty(routeAddressBlacklist)) {requestTemplate.header("n-d-address-blacklist", new String[]{routeAddressBlacklist});}}}}private void interceptOutputHeader(RequestTemplate requestTemplate) {if (this.interceptDebugEnabled) {System.out.println("-------- Feign Intercept Output Header Information ---------");Map<String, Collection<String>> headers = requestTemplate.headers();Iterator var3 = headers.entrySet().iterator();while(var3.hasNext()) {Entry<String, Collection<String>> entry = (Entry)var3.next();String headerName = (String)entry.getKey();boolean isHeaderContains = this.isHeaderContains(headerName.toLowerCase());if (isHeaderContains) {Collection<String> headerValue = (Collection)entry.getValue();System.out.println(headerName + "=" + headerValue);}}System.out.println("------------------------------------------------------------");}}protected InterceptorType getInterceptorType() {return InterceptorType.FEIGN;}
}

Gateway 通过 GlobalFilter

gateway

public interface GlobalFilter {Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

com.nepxion.discovery.plugin.strategy.gateway.filter.AbstractGatewayStrategyRouteFilter

public abstract class AbstractGatewayStrategyRouteFilter implements GatewayStrategyRouteFilter {@Autowiredprotected PluginAdapter pluginAdapter;@Autowiredprotected StrategyWrapper strategyWrapper;@Autowired(required = false)protected GatewayStrategyMonitor gatewayStrategyMonitor;@Value("${spring.application.strategy.gateway.header.priority:true}")protected Boolean gatewayHeaderPriority;@Value("${spring.application.strategy.gateway.original.header.ignored:true}")protected Boolean gatewayOriginalHeaderIgnored;@Value("${spring.application.strategy.gateway.core.header.transmission.enabled:true}")protected Boolean gatewayCoreHeaderTransmissionEnabled;@Value("${spring.application.strategy.gateway.route.filter.order:9000}")protected Integer filterOrder;public AbstractGatewayStrategyRouteFilter() {}public int getOrder() {return this.filterOrder;}public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {GatewayStrategyContext.getCurrentContext().setExchange(exchange);ServerHttpRequest request = exchange.getRequest();Builder requestBuilder = request.mutate();this.applyInnerHeader(request, requestBuilder);this.applyOuterHeader(request, requestBuilder);if (this.gatewayStrategyMonitor != null) {this.gatewayStrategyMonitor.monitor(exchange);}String path = request.getPath().toString();if (path.contains("inspector/inspect")) {GatewayStrategyFilterResolver.setHeader(request, requestBuilder, "endpoint-inspector-inspect", this.pluginAdapter.getPluginInfo((String)null), true);}ServerHttpRequest newRequest = requestBuilder.build();ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();GatewayStrategyContext.getCurrentContext().setExchange(newExchange);return chain.filter(newExchange);}private void applyInnerHeader(ServerHttpRequest request, Builder requestBuilder) {GatewayStrategyFilterResolver.setHeader(request, requestBuilder, "n-d-service-group", this.pluginAdapter.getGroup(), this.gatewayHeaderPriority);GatewayStrategyFilterResolver.setHeader(request, requestBuilder, "n-d-service-type", this.pluginAdapter.getServiceType(), false);String serviceAppId = this.pluginAdapter.getServiceAppId();if (StringUtils.isNotEmpty(serviceAppId)) {GatewayStrategyFilterResolver.setHeader(request, requestBuilder, "n-d-service-app-id", serviceAppId, false);}GatewayStrategyFilterResolver.setHeader(request, requestBuilder, "n-d-service-id", this.pluginAdapter.getServiceId(), false);GatewayStrategyFilterResolver.setHeader(request, requestBuilder, "n-d-service-address", this.pluginAdapter.getHost() + ":" + this.pluginAdapter.getPort(), false);GatewayStrategyFilterResolver.setHeader(request, requestBuilder, "n-d-service-version", this.pluginAdapter.getVersion(), false);GatewayStrategyFilterResolver.setHeader(request, requestBuilder, "n-d-service-region", this.pluginAdapter.getRegion(), false);GatewayStrategyFilterResolver.setHeader(request, requestBuilder, "n-d-service-env", this.pluginAdapter.getEnvironment(), false);GatewayStrategyFilterResolver.setHeader(request, requestBuilder, "n-d-service-zone", this.pluginAdapter.getZone(), false);}private void applyOuterHeader(ServerHttpRequest request, Builder requestBuilder) {String routeEnvironment = this.getRouteEnvironment();if (StringUtils.isNotEmpty(routeEnvironment)) {GatewayStrategyFilterResolver.setHeader(request, requestBuilder, "n-d-env", routeEnvironment, false);}if (this.gatewayCoreHeaderTransmissionEnabled) {Map<String, String> headerMap = this.strategyWrapper.getHeaderMap();String routeAddress;String routeVersionWeight;if (MapUtils.isNotEmpty(headerMap)) {Iterator var5 = headerMap.entrySet().iterator();while(var5.hasNext()) {Entry<String, String> entry = (Entry)var5.next();routeAddress = (String)entry.getKey();routeVersionWeight = (String)entry.getValue();GatewayStrategyFilterResolver.setHeader(request, requestBuilder, routeAddress, routeVersionWeight, this.gatewayHeaderPriority);}}String routeVersion = this.getRouteVersion();String routeRegion = this.getRouteRegion();routeAddress = this.getRouteAddress();routeVersionWeight = this.getRouteVersionWeight();String routeRegionWeight = this.getRouteRegionWeight();String routeIdBlacklist = this.getRouteIdBlacklist();String routeAddressBlacklist = this.getRouteAddressBlacklist();if (StringUtils.isNotEmpty(routeVersion)) {GatewayStrategyFilterResolver.setHeader(request, requestBuilder, "n-d-version", routeVersion, this.gatewayHeaderPriority);} else {GatewayStrategyFilterResolver.ignoreHeader(requestBuilder, "n-d-version", this.gatewayHeaderPriority, this.gatewayOriginalHeaderIgnored);}if (StringUtils.isNotEmpty(routeRegion)) {GatewayStrategyFilterResolver.setHeader(request, requestBuilder, "n-d-region", routeRegion, this.gatewayHeaderPriority);} else {GatewayStrategyFilterResolver.ignoreHeader(requestBuilder, "n-d-region", this.gatewayHeaderPriority, this.gatewayOriginalHeaderIgnored);}if (StringUtils.isNotEmpty(routeAddress)) {GatewayStrategyFilterResolver.setHeader(request, requestBuilder, "n-d-address", routeAddress, this.gatewayHeaderPriority);} else {GatewayStrategyFilterResolver.ignoreHeader(requestBuilder, "n-d-address", this.gatewayHeaderPriority, this.gatewayOriginalHeaderIgnored);}if (StringUtils.isNotEmpty(routeVersionWeight)) {GatewayStrategyFilterResolver.setHeader(request, requestBuilder, "n-d-version-weight", routeVersionWeight, this.gatewayHeaderPriority);} else {GatewayStrategyFilterResolver.ignoreHeader(requestBuilder, "n-d-version-weight", this.gatewayHeaderPriority, this.gatewayOriginalHeaderIgnored);}if (StringUtils.isNotEmpty(routeRegionWeight)) {GatewayStrategyFilterResolver.setHeader(request, requestBuilder, "n-d-region-weight", routeRegionWeight, this.gatewayHeaderPriority);} else {GatewayStrategyFilterResolver.ignoreHeader(requestBuilder, "n-d-region-weight", this.gatewayHeaderPriority, this.gatewayOriginalHeaderIgnored);}if (StringUtils.isNotEmpty(routeIdBlacklist)) {GatewayStrategyFilterResolver.setHeader(request, requestBuilder, "n-d-id-blacklist", routeIdBlacklist, this.gatewayHeaderPriority);} else {GatewayStrategyFilterResolver.ignoreHeader(requestBuilder, "n-d-id-blacklist", this.gatewayHeaderPriority, this.gatewayOriginalHeaderIgnored);}if (StringUtils.isNotEmpty(routeAddressBlacklist)) {GatewayStrategyFilterResolver.setHeader(request, requestBuilder, "n-d-address-blacklist", routeAddressBlacklist, this.gatewayHeaderPriority);} else {GatewayStrategyFilterResolver.ignoreHeader(requestBuilder, "n-d-address-blacklist", this.gatewayHeaderPriority, this.gatewayOriginalHeaderIgnored);}} else {GatewayStrategyFilterResolver.ignoreHeader(requestBuilder, "n-d-version");GatewayStrategyFilterResolver.ignoreHeader(requestBuilder, "n-d-region");GatewayStrategyFilterResolver.ignoreHeader(requestBuilder, "n-d-address");GatewayStrategyFilterResolver.ignoreHeader(requestBuilder, "n-d-version-weight");GatewayStrategyFilterResolver.ignoreHeader(requestBuilder, "n-d-region-weight");GatewayStrategyFilterResolver.ignoreHeader(requestBuilder, "n-d-id-blacklist");GatewayStrategyFilterResolver.ignoreHeader(requestBuilder, "n-d-address-blacklist");}}public PluginAdapter getPluginAdapter() {return this.pluginAdapter;}
}

通过IRule

com.netflix.loadbalancer.IRule

public interface IRule{public Server choose(Object key);public void setLoadBalancer(ILoadBalancer lb);public ILoadBalancer getLoadBalancer();
}

通过 pluginAdapter 可以拿到需要的数据

 //TODO 灰度测试@Autowiredprivate PluginAdapter pluginAdapter;@RequestMapping(value = "/gray/{value}", method = RequestMethod.GET)public String gray(@PathVariable(value = "value") String value) {value = pluginAdapter.getPluginInfo(value);value = couponsFeignService.gray(value);log.info("调用路径:{}", value);return value;}

MSE 微服务治理全链路灰度 太贵

阿里云MSE服务治理产品就是一款基于Java Agent实现的无侵入式企业生产级服务治理产品,您不需要修改任何一行业务代码,即可拥有不限于全链路灰度的治理能力,并且支持近 5年内所有的 Spring Boot、Spring Cloud和Dubbo

https://help.aliyun.com/document_detail/359851.html

Spring Cloud(十四):微服务灰度发布 --- Discovery相关推荐

  1. 《深入理解 Spring Cloud 与微服务构建》第十七章 使用 Spring Cloud OAuth2 保护微服务系统

    <深入理解 Spring Cloud 与微服务构建>第十七章 使用 Spring Cloud OAuth2 保护微服务系统 文章目录 <深入理解 Spring Cloud 与微服务构 ...

  2. 微服务等于 Spring Cloud?了解微服务架构和框架

    点击上方"芋道源码",选择"设为星标" 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 8:55 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | J ...

  3. Spring Cloud Alibaba 大型微服务项目实战

    作者介绍 程序员十三,多年一线开发经验,历任高级开发工程师.后端主程.技术部门主管等职位.同时也是开源项目的爱好者和贡献者.掘金优秀作者.CSDN 博客专家.实体图书作者.专栏作者.视频讲师. 小册介 ...

  4. 【福利】赠书:Spring Cloud与Docker微服务架构实战(第2版)

    本次福利送出好友周立的第二版书籍! 正在关注和使用Spring Cloud的朋友们不要错过哦! 内容提要 <Spring Cloud与Docker微服务架构实战(第2版)>基于Spring ...

  5. 《Spring Cloud与Docker微服务架构实战》配套代码

    不才写了本使用Spring Cloud玩转微服务架构的书,书名是<Spring Cloud与Docker微服务架构实战> - 周立,已于2017-01-12交稿.不少朋友想先看看源码,现将 ...

  6. Spring Cloud(5)---基于 Spring Cloud 完整的微服务架构实战

    基于 Spring Cloud 完整的微服务架构实战 技术栈 Spring boot - 微服务的入门级微框架,用来简化 Spring 应用的初始搭建以及开发过程. Eureka - 云端服务发现,一 ...

  7. Spring Cloud Hoxton 版本微服务项目搭建 admin 监控客户端

    Spring Cloud Hoxton 版本微服务项目搭建 admin 监控客户端 前言 在上一篇文章博主已经讲解了admin 管理中心服务项目如何创建,不会的话可以前往学习,传送门:Spring C ...

  8. Spring Cloud与Docker微服务架构实战 PDF版 内含目录

    Spring Cloud与Docker微服务架构实战  目录 1 微服务架构概述 1 1.1 单体应用架构存在的问题1 1.2 如何解决单体应用架构存在的问题3 1.3 什么是微服务3 1.4 微服务 ...

  9. Spring Cloud OAuth2 JWT 微服务认证服务器得构建

    文章目录 Spring Cloud OAuth2 JWT 微服务认证服务器得构建 前言 认证服务得搭建 `AuthorizationServer` `WebSecurityConfig` `Autho ...

最新文章

  1. android window 大小,android popupWindow 中宽度莫名很大,求帮助?
  2. 取消对 null 指针“l”的引用。_C++中的引用
  3. Eclipse launch failed.Binary not found解决方案
  4. c++引用专题之普通引用
  5. python基本数据类型包括哪些_python入门3——基本数据类型
  6. ai进入轮廓模式怎么退出_详解AI中扩展、扩展外观、轮廓化描边、创建轮廓
  7. java创建树结构_Java学习之XML-017
  8. LeetCode 第 22 场双周赛(220/2041,前10.8%)
  9. java+mysql学生学籍后台管理系统源码
  10. python来构建多层网络
  11. python枚举类型_Python 的枚举类型
  12. vs2002 vs2003 可能存在的问题以及解决办法!
  13. 流程图函数’怎么画_程序员必备画图技能之——流程图
  14. 在本地电脑运行vue-element-admin
  15. 基于随机森林、svm、CNN机器学习的风控欺诈识别模型
  16. 【Linux】ssh连接远程服务器
  17. Android毕业设计答辩会问什么问题,毕业论文答辩一般会问什么问题怎么回答
  18. 跨境电商小白:一件代发是什么?为什么要选择Starday一件代发?
  19. 百度地图api自定义修改地图背景样式
  20. 【Linux】指令与权限

热门文章

  1. 从语义网的角度看聊天机器人的产生
  2. 树莓派u盘启动并扩容
  3. macOS Chrome无法访问自签名https页面问题的解决办法
  4. qlv如何转换为mp4格式?怎样将qlv转换成mp4格式?
  5. python3 高效实现 最大质因数/质因数集合 方法
  6. 第三章数程序设计初步--分支结构项目3利息计算器
  7. 怎样区别桃花、杏花、梨花、樱花、梅花、李花
  8. 关于DPABI头动参数问题
  9. WordPress DUX主题顶部添加彩色美化条
  10. ShardingSphere5.1.0 +JPA的分表配置