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

news2024/12/31 5:13:35
  • 灰度发布
  • 微服务全链路灰度
  • 全链路灰度设计思路
    • 标签路由
    • 节点打标
    • 流量染色
    • 分布式链路追踪 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:8848
     metadata:
       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-gateway
    strategy:
      gateway:
        dynamic:
          route:
            enabled: true #开启网关订阅配置中心的动态路由策略,默认为false
   cloud:
      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-member
  cloud:
    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>
        <!-- <version‐weight>{"demomall‐member":"1.0=90;1.1=10", "demomall‐promotion":"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"?>
<!-- 在nacos配置中心修改网关控制的灰度发布策略 -->
<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">{"demomall‐member":"1.1", "demomall‐promotion":"1.1"}</route>
        <route id="stable‐route" type="version">{"demomall‐member":"1.0", "demomall‐promotion":"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">{"demomall‐member":"1.1", "tuli ngmall‐promotion":"1.1"}</route>
        <route id="stable‐route" type="version">{"demomall‐member":"1.0", "tu lingmall‐promotion":"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 {
    @Autowired
    protected 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 {
    @Autowired
    protected PluginAdapter pluginAdapter;
    @Autowired
    protected 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 灰度测试
 @Autowired
 private 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
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/63712.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[附源码]JAVA毕业设计论文管理系统(系统+LW)

[附源码]JAVA毕业设计论文管理系统&#xff08;系统LW&#xff09; 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a;…

Allegro如何使用快捷键快速切换层面操作指导

Allegro如何使用快捷键快速切换层面操作指导 Allegro可以用快捷键快速切换层面,比如在command下方输入数字1,可以自动打开top层 具体操作如下 打开系统属性,选择环境变量 找到home的环境变量的路径是哪里 找到路径下的pcbenv文件夹 找到env文件 用记事本打开env文件…

自学软件测试,学到什么程度可以出去找工作?

其实初级测试学的东西并不多&#xff0c;如果脱产学习的话2~3个月差不多就能简单入门。 另外不要担心&#xff0c;初级测试对于Python/Java编程&#xff0c;自动化测试&#xff0c;性能测试这些都是初步的了解和学习。如果说要深度掌握&#xff0c;那确实是还需要很多时间。 …

【pen200-lab】10.11.1.10

pen200-lab 学习笔记 【pen200-lab】10.11.1.10 🔥系列专栏:pen200-lab 🎉欢迎关注🔎点赞👍收藏⭐️留言📝 📆首发时间:🌴2022年12月5日🌴 🍭作者水平很有限,如果发现错误,还望告知,感谢! 文章目录 pen200-lab 学习笔记信息收集拓展信息收集 只开放了…

61张图,图解Spring事务,拆解底层源码

下面我会简单介绍一下 Spring 事务的基础知识&#xff0c;以及使用方法&#xff0c;然后直接对源码进行拆解。 不 BB&#xff0c;上文章目录。 ​1. 项目准备 需要搭建环境的同学&#xff0c;代码详见&#xff1a;https://github.com/lml200701158/program_demo/tree/main/spr…

09-18-k8s-二进制方式搭建

09-k8s-二进制方式搭建&#xff1a; 1、创建多台虚拟机&#xff0c;安装Linux操作系统 &#xff08;1&#xff09;一台或多台机器&#xff0c;操作系统 CentOS7.x-86_x64 &#xff08;2&#xff09;硬件配置&#xff1a;2GB 或更多 RAM&#xff0c;2 个 CPU 或更多 CPU&…

大数据_数据中台_数据汇聚联通

目录 一、数据采集、汇聚的方法和工具 1、线上行为采集 2、线下行为采集 3、互联网数据采集 4、内部数据汇聚 二、数据交换产品 1、数据源管理 2、离线数据交换 3、实时数据交换 三、数据存储的选择 1、在线与离线 2、OLTP与OLAP 3、存储技术 构建企业级的数据中台…

Java swing(GUI) mysql实现的仓库进销存管理系统源码+运行教程

今天给大家介绍下由Java swing mysql实现的一款仓库库存管理系统&#xff0c;该系统实现了基本的仓库进退货管理、用户管理等操作&#xff0c;主要涉及的知识点有&#xff1a;Java swing awt界面编程、数据库的基本操作&#xff08;增删改查&#xff09;&#xff0c;多线程等&a…

第十二章:synchronized与锁升级

相关面试题锁优化背景Synchronized 锁性能变化jdk5 以前复习&#xff1a;为什么任意一个对象都能成为锁&#xff1f;jdk6 之后synchronized的种类以及锁升级流程锁升级流程无锁偏向锁是什么作用小总结偏向锁的持有偏向锁 JVM 参数说明多线程环境下总结轻量级锁轻量级锁的获取代…

Java成员方法的声明和调用

声明成员方法可以定义类的行为&#xff0c;行为表示一个对象能够做的事情或者能够从一个对象取得的信息。类的各种功能操作都是用方法来实现的&#xff0c;属性只不过提供了相应的数据。 一个完整的方法通常包括方法名称、方法主体、方法参数和方法返回值类型&#xff0c;其结…

6-脱氧-β- L -半乳吡喃糖基鸟苷 5′-二磷酸,Guanosine 5‘-diphospho-fucose,GDP-BETA-L-FUCOSE

产品名称&#xff1a;6-脱氧-β- L -半乳吡喃糖基鸟苷 5′-二磷酸&#xff0c;GDP-L-岩藻糖&#xff0c;GDP-L-FUCOSE二钠盐 英文名称&#xff1a;Guanosine 5-diphospho-fucose&#xff0c;GDP-BETA-L-FUCOSE&#xff0c;GDP-L-Fuc.2Na CAS号:148296-47-3 英文同义词:Guanos…

【深度学习】常用算法生成对抗网络、自编码网络、多层感知机、反向传播等讲解(图文解释 超详细)

觉得有帮助请点赞关注收藏~~~ 一、生成对抗网络GAN Generative Adversarial Network 两个组件组成&#xff1a;一个生成器&#xff0c;用于生成虚拟数据&#xff0c;另一个是鉴别器&#xff0c;用于(GAN)生成式深度学习算法&#xff0c;可创建类似于训练数据的新数据实例。 G…

2022-kaggle-nlp赛事:Feedback Prize - English Language Learning

文章目录零、比赛介绍0.1 比赛目标0.2 数据集0.3 注意事项一、设置1.1 导入相关库1.2 设置超参数和随机种子1.3 启动wandb二、 数据预处理2.1 定义前处理函数&#xff0c;tokenizer文本2.2 定义Dataset&#xff0c;并将数据装入DataLoader三、辅助函数四、池化五、模型六、定义…

jmeter-事务控制器与并发控制器与if控制器项目实践

前言 在做性能压测的时候&#xff0c;除了做单接口这种基准压测&#xff0c;我们还需要多接口串联的混合场景&#xff0c;比如打开小程序展示的首页&#xff0c;购物下单时的结算页。如果这些接口都是串行的&#xff0c;那就非常简单了&#xff0c;仅仅只需要创建事务控制器&a…

【GD32F427开发板试用】+rtt-thread nano+finsh极简开发

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动&#xff0c;更多开发板试用活动请关注极术社区网站。作者&#xff1a;理想三旬 引言 在工作闲暇之际&#xff0c;逛逛论坛&#xff0c;无意间看到GD的试用活动&#xff0c;一如既往的积极&#xff0c;在官方还没发…

Linux 驱动的内核适配 - 方法

原生与野生 Linux 的驱动代码大致可分为两种&#xff1a;一种是已经进入 mainline 的&#xff0c;当内核 API 变化时&#xff0c;会被同步地修改&#xff1b;还有一种是 out-of-tree 的&#xff0c;需要用一套驱动代码去适配不同版本的内核。由于内核 API 持续变动的特性&…

带你实现react源码的核心功能

React 的几种组件以及首次渲染实现React 更新机制的实现以及 React diff 算法 React 的代码还是非常复杂的&#xff0c;虽然这里是一个简化版本。但是还是需要有不错的面向对象思维的。React 的核心主要有一下几点。 虚拟 dom 对象&#xff08;Virtual DOM&#xff09;虚拟 d…

RabbitMQ_消息确认机制

消息确认机制分为消息发送确认机制与消息消费确认机制 消息发送确认机制 消息发送确认机制&#xff1a;消息由producer发送后&#xff0c;确认其是否到达broker&#xff0c;又是否被exchange转发至对应queue的机制 该机制分为两部分&#xff1a;producer---broker&#xff0c…

Android 性能优化之内存优化——重识内存

我们知道&#xff0c;手机的内存是有限的&#xff0c;如果应用内存占用过大&#xff0c;轻则引起卡顿&#xff0c;重则导致应用崩溃或被系统强制杀掉&#xff0c;更严重的情况下会影响应用的留存率。因此&#xff0c;内存优化是性能优化中非常重要的一部分。但是&#xff0c;很…

66-86-javajvm-堆

66-javajvm-堆&#xff1a; 堆的核心概述 堆与进程、线程 一个进程对应一个JVM实例一个JVM实例对应一个堆空间进程包含多个线程&#xff0c;所以线程之间共享同一个堆空间 对堆的认识 一个JVM实例只存在一个堆内存&#xff0c;堆也是Java内存管理的核心区域。Java堆区在JVM启动…