Discovery基于apollo自定义规则蓝绿测试

Discovery基于apollo自定义规则蓝绿测试

系统架构

image-20210725111826442

系统配置

app-gateway配置

pom
<dependency>
    <groupId>com.nepxion</groupId>
    <artifactId>discovery-plugin-register-center-starter-eureka</artifactId>
</dependency>
<dependency>
    <groupId>com.nepxion</groupId>
    <artifactId>discovery-plugin-config-center-starter-apollo</artifactId>
</dependency>
<dependency>
    <groupId>com.nepxion</groupId>
    <artifactId>discovery-plugin-admin-center-starter</artifactId>
</dependency>
<dependency>
    <groupId>com.nepxion</groupId>
    <artifactId>discovery-plugin-strategy-starter-gateway</artifactId>
</dependency>
apollo

为了不对本地有影响,创建集群gray,连接本地eureka,新增如下配置

img

server.port = 8905
spring.application.name = app-gateway

spring.profiles.active = test

spring.cloud.gateway.routes[1].id = app-infra
spring.cloud.gateway.routes[1].uri = lb://app-infra
spring.cloud.gateway.routes[1].predicates[0] = Path=/app/infra/**
spring.cloud.gateway.routes[1].predicates[1].name = RequestBodyCachePredicateFactory
spring.cloud.gateway.routes[1].predicates[1].args.inClass = #{T(String)}
spring.cloud.gateway.routes[1].predicates[1].args.predicate = #{@configPredicate}
spring.cloud.gateway.routes[1].filters[0] = StripPrefix=1

##中台
spring.cloud.gateway.routes[2].id = app-middle
spring.cloud.gateway.routes[2].uri = lb://app-middle
spring.cloud.gateway.routes[2].predicates[0] = Path=/app/middle/**
spring.cloud.gateway.routes[2].predicates[1].name = RequestBodyCachePredicateFactory
spring.cloud.gateway.routes[2].predicates[1].args.inClass = #{T(String)}
spring.cloud.gateway.routes[2].predicates[1].args.predicate = #{@configPredicate}
spring.cloud.gateway.routes[2].filters[0] = StripPrefix=1


eureka.instance.prefer-ip-address = true
eureka.client.service-url.defaultZone = http://127.0.0.1:8761/eureka
#服务默认注册状态
eureka.instance.initialStatus = UP
#客户端从注册中心拉取实例信息的频率,单位为s
eureka.client.registryFetchIntervalSeconds = 2
#ribbon刷新本地缓存的频率,单位ms
ribbon.ServerListRefreshInterval = 2000

spring.codec.max-in-memory-size = 2MB

##### 灰度相关配置
###灰度发布测试
spring.cloud.discovery.reactive.enabled = false
spring.cloud.gateway.discovery.locator.enabled = true
spring.cloud.gateway.discovery.locator.lowerCaseServiceId = true
### 元数据组
eureka.instance.metadataMap.group = app-group
## 调试日志
spring.application.strategy.monitor.enabled = true
spring.application.strategy.rest.intercept.debug.enabled = true
spring.application.strategy.logger.debug.enabled = true

app-middle配置

pom
       <dependency>
         <groupId>com.nepxion</groupId>
         <artifactId>discovery-plugin-register-center-starter-eureka</artifactId>
      </dependency>
      <dependency>
         <groupId>com.nepxion</groupId>
         <artifactId>discovery-plugin-config-center-starter-apollo</artifactId>
      </dependency>
      <dependency>
         <groupId>com.nepxion</groupId>
         <artifactId>discovery-plugin-strategy-starter-service</artifactId>
      </dependency>
apollo
灰度实例

创建gray集群,复制基础信息,并添加如下配置

spring.application.name = app-middle
server.port = 9906
logging.level.root = info
spring.profiles.active = test

### 灰度配置
spring.application.strategy.monitor.enabled = true
spring.application.strategy.rest.intercept.debug.enabled = true
spring.application.strategy.logger.debug.enabled = true

spring.application.strategy.version.prefer.enabled = true

eureka.instance.metadataMap.group = app-group
eureka.instance.metadataMap.version = 1.1
eureka.instance.metadataMap.region = gray
eureka.instance.metadataMap.env = gray


eureka.client.serviceUrl.defaultZone = http://127.0.0.1:8761/eureka
# eureka注册优先使用ip地址
eureka.instance.prefer-ip-address = true
## 心跳间隔(默认30秒)
eureka.instance.leaseRenewalIntervalInSeconds = 5
# 定时刷新本地缓存时间
eureka.client.registryFetchIntervalSeconds = 5
# ribbon缓存时间
ribbon.ServerListRefreshInterval = 2000
# 连接超时时间(默认1秒)
feign.client.config.default.connectTimeout = 10000
# 读取超时时间(默认1秒)
feign.client.config.default.readTimeout = 12000

正常实例

创建normal集群,复制基础信息,并添加如下配置

spring.application.name = app-middle
server.port = 8906
logging.level.root = info
spring.profiles.active = test

##灰度配置
spring.application.strategy.monitor.enabled = true
spring.application.strategy.rest.intercept.debug.enabled = true
spring.application.strategy.logger.debug.enabled = true

spring.application.strategy.version.prefer.enabled = true
eureka.instance.metadataMap.group = app-group
eureka.instance.metadataMap.version = 1.0
eureka.instance.metadataMap.region = test
eureka.instance.metadataMap.env = test


eureka.client.serviceUrl.defaultZone = http://127.0.0.1:8761/eureka
# eureka注册优先使用ip地址
eureka.instance.prefer-ip-address = true
## 心跳间隔(默认30秒)
eureka.instance.leaseRenewalIntervalInSeconds = 5
# 定时刷新本地缓存时间
eureka.client.registryFetchIntervalSeconds = 5
# ribbon缓存时间
ribbon.ServerListRefreshInterval = 2000
# 连接超时时间(默认1秒)
feign.client.config.default.connectTimeout = 10000
# 读取超时时间(默认1秒)
feign.client.config.default.readTimeout = 12000

app-infra配置

pom
       <dependency>
         <groupId>com.nepxion</groupId>
         <artifactId>discovery-plugin-register-center-starter-eureka</artifactId>
      </dependency>
      <dependency>
         <groupId>com.nepxion</groupId>
         <artifactId>discovery-plugin-config-center-starter-apollo</artifactId>
      </dependency>
      <dependency>
         <groupId>com.nepxion</groupId>
         <artifactId>discovery-plugin-strategy-starter-service</artifactId>
      </dependency>
apollo
灰度实例

创建gray集群,复制基础信息,并添加如下配置

spring.application.name = app-infra
server.port = 9901
logging.level.root = info
spring.profiles.active = test

##灰度发布测试
spring.application.strategy.monitor.enabled = true
spring.application.strategy.rest.intercept.debug.enabled = true
spring.application.strategy.logger.debug.enabled = true

eureka.instance.metadataMap.group = app-group
eureka.instance.metadataMap.version = 1.1
eureka.instance.metadataMap.region = gray
eureka.instance.metadataMap.env = gray

eureka.client.serviceUrl.defaultZone = http://127.0.0.1:8761/eureka
# eureka注册优先使用ip地址
eureka.instance.prefer-ip-address = true
## 心跳间隔(默认30秒)
eureka.instance.leaseRenewalIntervalInSeconds = 5
# 定时刷新本地缓存时间
eureka.client.registryFetchIntervalSeconds = 5
# ribbon缓存时间
ribbon.ServerListRefreshInterval = 2000
# 连接超时时间(默认1秒)
feign.client.config.default.connectTimeout = 10000
# 读取超时时间(默认1秒)
feign.client.config.default.readTimeout = 12000

正常实例

创建normal集群,复制基础信息,并添加如下配置

spring.application.name = app-infra
server.port = 8901
logging.level.root = info
spring.profiles.active = test

#灰度发布调试
spring.application.strategy.monitor.enabled = true
spring.application.strategy.rest.intercept.debug.enabled = true
spring.application.strategy.logger.debug.enabled = true

eureka.instance.metadataMap.group = app-group
eureka.instance.metadataMap.version = 1.0
eureka.instance.metadataMap.region = test
eureka.instance.metadataMap.env = test

eureka.client.serviceUrl.defaultZone = http://127.0.0.1:8761/eureka
# eureka注册优先使用ip地址
eureka.instance.prefer-ip-address = true
## 心跳间隔(默认30秒)
eureka.instance.leaseRenewalIntervalInSeconds = 5
# 定时刷新本地缓存时间
eureka.client.registryFetchIntervalSeconds = 5
# ribbon缓存时间
ribbon.ServerListRefreshInterval = 2000
# 连接超时时间(默认1秒)
feign.client.config.default.connectTimeout = 10000
# 读取超时时间(默认1秒)
feign.client.config.default.readTimeout = 12000

app-gateway自定义灰度路由规则配置

在app-gateway中编写如下代码

package cn.example.app.gateway.gray;

import com.nepxion.discovery.plugin.strategy.gateway.filter.DefaultGatewayStrategyRouteFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;

/**
 * 适用于A/B Testing或者更根据某业务参数决定蓝绿灰度路由路径。可以结合配置中心分别配置A/B两条路径,可以动态改变并通 知// 当Header中传来的用户为张三,执行一条路由路径;为李四,执行另一条路由路径
 */
@Slf4j
public class MyGatewayStrategyRouteFilter extends DefaultGatewayStrategyRouteFilter {
    //兜底路由
    private static final String DEFAULT_A_ROUTE_VERSION = "{\"app-middle\":\"1.0\", \"app-infra\":\"1.0\"}";
    @Value("${blue.route.version:" + DEFAULT_A_ROUTE_VERSION + "}")
    private String blueRouteVersion;
    @Value("${green.route.version:" + DEFAULT_A_ROUTE_VERSION + "}")
    private String greenRouteVersion;


    @Override
    public String getRouteVersion() {
    String version = strategyContextHolder.getHeader("APP-VERSION");
    log.info("自定义灰度规则 version:{}", version);
    if (version.equals("v1.0")) {
        log.info("执行全链路路由:{}", blueRouteVersion);
        return blueRouteVersion;
    }
    if (version.equals("v1.1")) {
        log.info("执行全链路路由:{}", greenRouteVersion);
        return greenRouteVersion;
    }
    return DEFAULT_A_ROUTE_VERSION;
}
}

在配置类里@Bean方式进行过滤类创建,覆盖框架内置的过滤类

package cn.example.app.gateway;

import cn.example.app.gateway.gray.MyGatewayStrategyRouteFilter;
import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
import com.nepxion.discovery.plugin.strategy.gateway.filter.GatewayStrategyRouteFilter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@EnableApolloConfig
@SpringBootApplication
public class AppGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(AppGatewayApplication.class, args);
    }

    //自定义网关
    @Bean
    public GatewayStrategyRouteFilter serviceStrategyRouteFilter() {
        return new MyGatewayStrategyRouteFilter();
    }
}

在app-gateway的apollo中增加如下配置

##蓝路由
blue.route.version = {"app-middle":"1.0", "app-infra":"1.0"}
##绿路由
green.route.version = {"app-middle":"1.1", "app-infra":"1.1"}

编写测试接口

app-infra


@RestController
@RequestMapping("/test/")
@Slf4j
public class DeployTestController {

    @Autowired
    private PluginAdapter pluginAdapter;

    public String getPluginInfo(String value) {
        return pluginAdapter.getPluginInfo(value);
    }

    /**
    *
    *此接口返回当前服务的元数据信息包括:版本、端口、区域、环境等
    **/
    @GetMapping(path = "/rest/{value}")
    public String rest(@PathVariable(value = "value") String value) {
        value = getPluginInfo(value);
        log.info("调用路径:{}", value);
        return value;
    }


}

编写feign client,并deploy

package cn.example.infra.client;

@FeignClient("app-infra")
@RequestMapping("/test/")
public interface InfraTestClient {
    @GetMapping(path = "/rest/{value}")
    String rest(@PathVariable(value = "value") String value);
}

app-middle

配置pom,并引入InfraTestClient调用rest方法

@RestController
@RequestMapping("/middle/test")
@Slf4j
public class TestController {
@Autowired
private InfraTestClient infraTestClient;

@Autowired
private PluginAdapter pluginAdapter;

public String getPluginInfo(String value) {
    return pluginAdapter.getPluginInfo(value);
}

    /**
    *
    *此接口返回当前服务的元数据信息包括:版本、端口、区域、环境等
    **/
@GetMapping(path = "/rest/{value}")
public String rest(@PathVariable(value = "value") String value) {
    value = getPluginInfo(value);
    value =infraTestClient.rest(value);
    log.info("调用路径:{}", value);

    return value;
}
}

测试

分别启动如下服务

image-20210725112126976

接口测试

##绿色请求
curl -X GET "http://127.0.0.1:8905/app/middle/test/rest/1" -H "accept: */*" -H "APP-VERSION: v1.1" 

## 蓝色请求
curl -X GET "http://127.0.0.1:8905/app/middle/test/rest/1" -H "accept: */*" -H "APP-VERSION: v1.0"

##兜底请求
curl -X GET "http://127.0.0.1:8905/app/middle/test/rest/1" -H "accept: */*" -H "APP-VERSION: v3.0"

在调用脚本请求之前,再看一眼apollo配置的规则

##蓝色请求
blue.route.version = {"app-middle":"1.0", "app-infra":"1.0"}
## 绿色请求
green.route.version = {"app-middle":"1.1", "app-infra":"1.1"}

测试结果

绿色请求,期望返回结果包含:v1.1

img

蓝色请求,期望返回结果包含:v1.0

img

兜底请求测试,期望返回结果包含:v1.0

img

到此整个实例完整结束。

QA

  1. 目前的配置方式,如果不通过网关调用,比如app-infra调用app-middle,这种配置是不是就不支持了?

    答:是的,如果服务内部之间调用这种就没法支持了,后续章节会给出对应的解决方案。

  2. 如果A部门的服务没有集成discovery调用B部门的服务app-infra,而app-infra正好有2个版本,那他是不是都有机会命中调用?这样是不是不合理?

    答:是的,都有机会命中,B部门应该提供稳定的app-infra供A部门的服务调用,后续章节会给出对应的解决方案。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值