Skip to content

6.2.0(SEP 20, 2020)

Compare
Choose a tag to compare
@HaojunRen HaojunRen released this 20 Sep 07:05

发布日志

  • 本次发布版本,深入和Spring Cloud AlibabaNacos团队进行探讨、合作、测试,并结合若干家公司的落地实践,进行优化和重构,以更强大的功能,解决使用者的真正痛点,以更开放的方式,供使用者灵活扩展
  • 欢迎使用Nepxion Polaris集成式脚手架,极大降低Nepxion Discovery接入成本,请访问 Polaris【北极星】企业级云原生微服务框架 :https://github.com/Nepxion/Polaris

发布策略

提醒:版本号右边, 表示>=该版本号, 表示<=该版本号

提醒:Spring Boot版本和Spring Cloud Alibaba版本需要在版本号后面加上.RELEASE

版本 状态 SC版本 SB版本 SCA版本
6.2.0 H.SR5 ↑
H
G
F
2.3.x
2.2.x
2.1.x
2.0.x
2.2.x
2.2.x
2.1.x
2.0.x
5.6.0 G 2.1.x 2.1.x
4.15.0 F 2.0.x 2.0.x
3.19.0 E 1.5.x 1.5.x
2.0.x D 1.x.x N/A
1.0.x C 1.x.x N/A

表示维护中 | 表示不维护,但可用,强烈建议升级 | 表示不维护,不可用,已废弃

  • 6.x.x版本(同时适用于Finchley、Greenwich和Hoxton以及未来的更高版本),将继续维护
  • 5.x.x版本(适用于Greenwich)已废弃
  • 4.x.x版本(适用于Finchley)已废弃
  • 3.x.x版本(适用于Edgware)不维护,但可用,强烈建议升级
  • 2.x.x版本(适用于Dalston)已废弃
  • 1.x.x版本(适用于Camden)已废弃

版本变更

本次版本升级了很多中间件的版本号,但不需要担心,都可以降级。使用者保持老的中间件版本号即可,无缝兼容老版本

① 不兼容项

  • 旧的注册中心插件引入方式,如下
<dependency>
    <groupId>com.nepxion</groupId>
    <artifactId>discovery-plugin-starter-nacos</artifactId>
    <!-- <artifactId>discovery-plugin-starter-eureka</artifactId> -->
    <!-- <artifactId>discovery-plugin-starter-consul</artifactId> -->
    <!-- <artifactId>discovery-plugin-starter-zookeeper</artifactId> -->
</dependency>
  • 新的注册中心插件引入方式,如下(中间多了register-center,即显式表达为注册中心的含义)
<dependency>
    <groupId>com.nepxion</groupId>
    <artifactId>discovery-plugin-register-center-starter-nacos</artifactId>
    <!-- <artifactId>discovery-plugin-register-center-starter-eureka</artifactId> -->
    <!-- <artifactId>discovery-plugin-register-center-starter-consul</artifactId> -->
    <!-- <artifactId>discovery-plugin-register-center-starter-zookeeper</artifactId> -->
</dependency>

② 框架变更

  • 默认集成Spring Cloud Hoxton.SR8(可降级
  • 默认集成Spring Boot到2.3.4.RELEASE(只支持Hoxton.SR5及以上的版本),该版本支持Docker分层,极大提高部署效率(可降级
  • 默认集成Spring Cloud Alibaba 2.2.3.RELEASE(可降级
  • 默认集成Nacos 1.3.3(可降级
  • 默认集成Sentinel 1.8.0(可降级
  • 默认集成Apollo 1.7.0(可降级
  • 默认集成Skywalking 8.1.0(可降级
  • 默认集成Spring Boot Admin 2.3.0(可降级
  • 默认集成Guava到29.0-jre(可降级
  • 默认集成Caffeine到2.8.5(可降级
  • 升级Matrix到2.0.8
  • 升级Eventbus到2.0.13
  • 优化Sentinel的引入
  • 移除Opentracing Spring Cloud Starter的引入,缩小引入范围,只引入Opentracing Api
  • 移除Opentracing Skywalking Version的引入,改为Skywalking Version,可读性更强一些
  • 移除Nacos和Sentinel版本在Pom里的显式定义,避免换Sring Cloud Alibaba版本时候引起冲突
  • 移除jboss-logging相关日志包的引用
  • 移除未用到的log4j2和disruptor相关日志包的引用
  • 改进了一些中间件版本的潜在冲突,尽量跟主流版本对齐

③ 版本升降

  • 通过如下方式,对Spring Cloud、Spring Boot和Spring Cloud Alibaba版本进行升降级
<properties>
    <!-- Spring Cloud Hoxton compatible versions -->
    <spring.cloud.version>Hoxton.SR8</spring.cloud.version>
    <spring.cloud.alibaba.version>2.2.3.RELEASE</spring.cloud.alibaba.version>
    <spring.boot.version>2.3.4.RELEASE</spring.boot.version>

    <!-- Spring Cloud Greenwich compatible versions -->
    <!-- <spring.cloud.version>Greenwich.SR6</spring.cloud.version>
    <spring.cloud.alibaba.version>2.1.3.RELEASE</spring.cloud.alibaba.version>
    <spring.boot.version>2.1.16.RELEASE</spring.boot.version> -->

    <!-- Spring Cloud Finchley compatible versions -->
    <!-- <spring.cloud.version>Finchley.SR4</spring.cloud.version>
    <spring.cloud.alibaba.version>2.0.3.RELEASE</spring.cloud.alibaba.version>
    <spring.boot.version>2.0.9.RELEASE</spring.boot.version> -->
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring.cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${spring.cloud.alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring.boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

③ 指南示例变更

  • 引入Opentracing Concurrent包支持异步埋点
  • 去掉Jaeger Client显式引入,通过Opentracing Spring Jaeger Starter引入它,避免造成不兼容
  • 简化Jaeger埋点包引入,只需要引入下面两个包即可
<dependency>
    <groupId>io.opentracing.contrib</groupId>
    <artifactId>opentracing-spring-cloud-starter</artifactId>
</dependency>

<dependency>
    <groupId>io.opentracing.contrib</groupId>
    <artifactId>opentracing-spring-jaeger-starter</artifactId>
</dependency>

功能迭代

增加动态变更元数据的灰度路由策略

该功能在Spring Cloud Alibaba的新版本上才被支持

利用注册中心的Open API接口动态变更服务实例的元数据,达到稳定版本和灰度版本流量灰度控制的目的。以Nacos的版本匹配为例

老的稳定版本的服务实例配置版本元数据,如下

spring.cloud.nacos.discovery.metadata.version=stable

新的稳定版本的服务实例配置版本元数据,如下

spring.cloud.nacos.discovery.metadata.version=gray

灰度路由策略,如下

表示所有的服务流量走灰度版本

<?xml version="1.0" encoding="UTF-8"?>
<rule>
    <strategy>
        <version>gray</version>
    </strategy>
</rule>

表示a服务流量走灰度版本,b服务流量走稳定版本

<?xml version="1.0" encoding="UTF-8"?>
<rule>
    <strategy>
        <version>{"discovery-guide-service-a":"gray", "discovery-guide-service-b":"stable"}</version>
    </strategy>
</rule>

也可以通过全链路传递Header方式实现

n-d-version=gray
n-d-version={"discovery-guide-service-a":"gray", "discovery-guide-service-b":"stable"}

新上线的服务实例版本为gray,即默认是灰度版本。等灰度成功后,通过注册中心的Open API接口变更服务版本为stable,或者在注册中心界面手工修改

  • Nacos Open API变更元数据
curl -X PUT 'http://ip:port/nacos/v1/ns/service?serviceName={appId}&metadata=version%3stable'
  • Eureka Open API变更元数据
curl -X PUT 'http://ip:port/eureka/apps/{appId}/{instanceId}/metadata?version=stable'
  • Consul Open API变更元数据

自行研究

  • Zookeeper Open API变更元数据

自行研究

需要注意

① 并非所有的注册中心都支持动态元数据变更方式,需要使用者自行研究

② 动态元数据变更方式利用第三方注册中心的Open API达到最终目的,其可能具有一定的延迟性,不如本框架那样具有灰度路由实时生效的特征,但比本框架动态变更灰度路由策略简单了一些

③ 动态元数据变更方式只是让新的元数据驻留在内存里,并不持久化。当服务重启后,服务的元数据仍旧会以初始值为准

增加全局唯一ID元数据

全局唯一ID对应于元数据spring.application.uuid字段,并为每个服务实例分配一个,注册到注册中心

增加服务下线实时性的流量绝对无损策略

服务下线场景中,由于Ribbon负载均衡组件存在着缓存机制,当被调用的服务实例已经下线,而调用的服务实例还暂时缓存着它,直到下个心跳周期才会把已下线的服务实例剔除,在此期间,会造成流量有损

框架提供流量的实时性的绝对无损。采用下线之前,把服务实例添加到屏蔽名单中,负载均衡不会去寻址该服务实例。下线之后,清除该名单。实现该方式,需要通过DevOps调用注册中心的Open API推送或者在注册中心界面手工修改,通过全局订阅方式实现,Group为discovery-guide-group,Data Id为discovery-guide-group(全局发布,两者都是组名)

  • 配置全局唯一ID屏蔽策略

通过服务全局唯一ID进行屏蔽。此用法适用于Docker和Kubernetes上IP地址不确定的场景,策略内容如下,采用如下两种方式之一均可

<?xml version="1.0" encoding="UTF-8"?>
<rule>
    <strategy-blacklist>
        <!-- 单个ID形式。如果多个用“;”分隔,不允许出现空格 -->
        <id value="e92edde5-0153-4ec8-9cbb-b4d3f415aa33;af043384-c8a5-451e-88f4-457914e8e3bc"/>

        <!-- 多个ID节点形式 -->
        <!-- <id value="e92edde5-0153-4ec8-9cbb-b4d3f415aa33"/>
        <id value="af043384-c8a5-451e-88f4-457914e8e3bc"/> -->
    </strategy-blacklist>
</rule>

也可以通过全链路传递Header方式实现:n-d-id-blacklist

  • 配置IP地址和端口屏蔽策略

通过IP地址或者端口或者IP地址+端口进行屏蔽,支持通配符方式。此用法适用于IP地址确定的场景,策略内容如下,采用如下两种方式之一均可

<?xml version="1.0" encoding="UTF-8"?>
<rule>
    <strategy-blacklist>
        <!-- 单个Address形式。如果多个用“;”分隔,不允许出现空格 -->
        <address value="192.168.43.101:1201;192.168.*.102;1301"/>

        <!-- 多个Address节点形式 -->
        <!-- <address value="192.168.43.101:1201"/>
        <address value="192.168.*.102"/>
        <address value="1301"/> -->
    </strategy-blacklist>
</rule>

也可以通过全链路传递Header方式实现:n-d-address-blacklist

增加Zone的全链路可用区亲和性隔离和路由

  • 可用区亲和性隔离。基于调用端实例和提供端实例的元数据Metadata的zone配置值相等实现隔离
  • 可用区亲和性路由。基于调用端实例找不到符合条件的提供端实例,把流量路由到其它可用区
# 启动和关闭可用区亲和性,即同一个可用区的服务才能调用,同一个可用区的条件是调用端实例和提供端实例的元数据Metadata的zone配置值必须相等。缺失则默认为false
# spring.application.zone.affinity.enabled=false
# 启动和关闭可用区亲和性失败后的路由,即调用端实例没有找到同一个可用区的提供端实例的时候,当开关打开,可路由到其它可用区或者不归属任何可用区,当开关关闭,则直接调用失败。缺失则默认为true
# spring.application.zone.route.enabled=true

增加数据库和消息队列灰度发布

通过订阅业务参数的变化,实现参数化灰度发布,例如,基于多Datasource的数据库灰度发布,基于多Queue的消息队列灰度发布

增加参数化灰度规则,Group为discovery-guide-group,Data Id为discovery-guide-group(全局发布,两者都是组名),规则内容如下,实现功能

  • 服务a在版本为1.0的时候,数据库的数据源指向db1;服务a在版本为1.1的时候,数据库的数据源指向db2
  • 服务b在区域为dev的时候,消息队列指向queue1;服务b在区域为dev的时候,消息队列指向queue2
  • 服务c在环境为env1的时候,数据库的数据源指向db1;服务c在环境为env2的时候,数据库的数据源指向db2
  • 服务d在可用区为zone1的时候,消息队列指向queue1;服务d在可用区为zone2的时候,消息队列指向queue2
  • 服务c在IP地址和端口为192.168.43.101:1201的时候,数据库的数据源指向db1;服务c在IP地址和端口为192.168.43.102:1201的时候,数据库的数据源指向db2
<?xml version="1.0" encoding="UTF-8"?>
<rule>
    <parameter>
        <service service-name="discovery-springcloud-example-a" tag-key="version" tag-value="1.0" key="ShardingSphere" value="db1"/>
        <service service-name="discovery-springcloud-example-a" tag-key="version" tag-value="1.1" key="ShardingSphere" value="db2"/>
        <service service-name="discovery-springcloud-example-b" tag-key="region" tag-value="dev" key="RocketMQ" value="queue1"/>
        <service service-name="discovery-springcloud-example-b" tag-key="region" tag-value="qa" key="RocketMQ" value="queue2"/>
        <service service-name="discovery-springcloud-example-c" tag-key="env" tag-value="env1" key="ShardingSphere" value="db1"/>
        <service service-name="discovery-springcloud-example-c" tag-key="env" tag-value="env2" key="ShardingSphere" value="db2"/>
        <service service-name="discovery-springcloud-example-d" tag-key="zone" tag-value="zone1" key="RocketMQ" value="queue1"/>
        <service service-name="discovery-springcloud-example-d" tag-key="zone" tag-value="zone2" key="RocketMQ" value="queue2"/>
        <service service-name="discovery-springcloud-example-e" tag-key="address" tag-value="192.168.43.101:1201" key="ShardingSphere" value="db1"/>
        <service service-name="discovery-springcloud-example-e" tag-key="address" tag-value="192.168.43.102:1201" key="ShardingSphere" value="db2"/>
    </parameter>
</rule>

通过事件总线方式,对参数改变动态实现监听,并在此类里自行对接相关的数据库和消息队列中间件的切换和驱动

@EventBus
public class MySubscriber {
    @Autowired
    private PluginAdapter pluginAdapter;

    @Subscribe
    public void onParameterChanged(ParameterChangedEvent parameterChangedEvent) {
        ParameterEntity parameterEntity = parameterChangedEvent.getParameterEntity();
        String serviceId = pluginAdapter.getServiceId();
        List<ParameterServiceEntity> parameterServiceEntityList = null;
        if (parameterEntity != null) {
            Map<String, List<ParameterServiceEntity>> parameterServiceMap = parameterEntity.getParameterServiceMap();
            parameterServiceEntityList = parameterServiceMap.get(serviceId);
        }
        System.out.println("========== 获取动态参数, serviceId=" + serviceId + ", parameterServiceEntityList=" + parameterServiceEntityList);
    }
}

启动和关闭在服务启动的时候参数订阅事件发送,使用者可以通过如下开关,决定在服务启动的时候,读到参数配置的时候,是否要发送一个事件触发数据库和消息队列中间件的切换

# 启动和关闭在服务启动的时候参数订阅事件发送。缺失则默认为true
spring.application.parameter.event.onstart.enabled=true

具体参考https://github.com/Nepxion/DiscoveryContrib里的实现方式

增加启动和关闭核心策略Header传递的开关

分别对应到Spring Cloud Gateway和Zuul网关,服务端的Feign和RestTemplate调用四个场景下的核心策略Header传递

# 启动和关闭核心策略Header传递,缺失则默认为true。当全局订阅启动时,可以关闭核心策略Header传递,这样可以节省传递数据的大小,一定程度上可以提升性能。核心策略Header,包含如下
# 1. n-d-version
# 2. n-d-region
# 3. n-d-address
# 4. n-d-version-weight
# 5. n-d-region-weight
# 6. n-d-id-blacklist
# 7. n-d-address-blacklist
# 8. n-d-env (不属于灰度蓝绿范畴的Header,只要外部传入就会全程传递)
spring.application.strategy.gateway.core.header.transmission.enabled=true
spring.application.strategy.zuul.core.header.transmission.enabled=true
spring.application.strategy.feign.core.header.transmission.enabled=true
spring.application.strategy.rest.template.core.header.transmission.enabled=true

该方案适合中小公司用,所有的服务共同订阅一个灰度蓝绿策略配置,服务间调用不需要传递灰度蓝绿策略的Header,即可实现灰度蓝绿功能,这种方案的优越性在异步调用中不需要考虑丢失Header的情况。同时该方案下,也支持业务传递自己的Header,根据业务Header进行表达式判断,同样可实现更加灵活的灰度蓝绿功能

支持控制台获取网关名列表接口

@RequestMapping(path = "/gateways", method = RequestMethod.GET)
@ApiOperation(value = "获取服务注册中心的网关名列表", notes = "", response = List.class, httpMethod = "GET")
@ResponseBody
public List<String> gateways() {
    return getGateways();
}

支持IDE里对配置的弹出提示

  • Idea编辑器的配置文件可以自动弹出Nepxion Discovery的配置提示
  • Eclipse编辑器的配置文件可以自动弹出Nepxion Discovery的配置提示

优化局部规则和全局规则的继承关系

  • 以前版本的逻辑是当局部规则和全局规则同时存在的时候,只取局部规则。
  • 现在版本的逻辑是,如果一个配置在局部和全局都存在,取局部里的对应配置,如果局部不存在,而全局存在,取全局里的对应配置

优化灰度规则策略订阅的日志输出

  • 从日志可以判断出是全局订阅还是局部订阅
  • 区分Nacos、Apollo、Redis的订阅Key的具体形式

优化Discovery Agent启动参数

  • 修复基于Spring Boot 2.x版本,在使用@Async注解方式的异步场景下,丢失灰度蓝绿Header的Bug
  • 不再需要在启动参数里配置 -Dthread.request.decorator.enabled,,该值从默认为false改成默认为true

优化Discovery Console界面

  • 显示env属性
  • 显示zone属性
  • 增加全局配置和局部配置分开显示

优化架构

  • 优化相关类结构
  • 优化相关类名
  • 优化相关注释
  • 增加@ConditionalOnMissingBean注解,核心对象通过protected方式暴露,便于使用者扩展

优化日志

  • 去除调用链在异步调用时候,Threadlocal里缺失Span的情况下频繁报异常日志的问题
  • 去除权重负载均衡时候,实例为空的情况下频繁报异常日志的问题

优化文档

  • 合并指南示例的文档到源码文档里
  • 重构源码文档
  • 增加极简示例新手入门文档

相关测试

自动化测试

  • 增加环境隔离和路由的自动化测试用例
  • 增加服务下线实时性的流量绝对无损的自动化测试用例
  • 增加Sentinel的自动化测试用例

以Sentinel的权限功能为例子,参考PolarisGuide,测试实现方式如下

① 远程配置中心约定

  • Nacos的Key格式
Group为DEFAULT_GROUP,Data ID为sentinel-authority-${spring.application.name}。每个服务都专享自己的Sentinel规则
  • Apollo的Key格式
namespace为application,Key为sentinel-authority。每个服务都专享自己的Sentinel规则

② 执行测试用例前,把执行限流降级熔断等逻辑的内容(executePath = "sentinel-authority-2.json")推送到远程配置中心

③ 执行测试用例,通过断言Assert来判断测试结果

④ 执行测试用例后,把修改过的内容(resetPath = "sentinel-authority-1.json")复原,再推送一次到远程配置中心

public class PolarisTestCases {
    @Autowired
    private TestRestTemplate testRestTemplate;

    @DTestConfig(group = "DEFAULT_GROUP", serviceId = "sentinel-authority-polaris-service-b", executePath = "sentinel-authority-2.json", resetPath = "sentinel-authority-1.json")
    public void testSentinelAuthority1(String testUrl) {
        int count = 0;
        for (int i = 0; i < 4; i++) {
            String result = testRestTemplate.postForEntity(testUrl, "gateway", String.class).getBody();

            LOG.info("Result{} : {}", i + 1, result);

            if (result.contains("AuthorityRule")) {
                count++;
            }
        }

        Assert.assertEquals(count, 4);
    }
}

集成测试

全方位对Spring Cloud Hoxton进行集成测试,发现如下问题

  • 策略下内置Header来决策蓝绿和灰度,可以代替外部传入Header,这块功能Spring Cloud Gateway在Finchley版不支持
  • OpenTracing对Finchley版的Spring Cloud Gateway的reactor-core包存在版本兼容性问题,如果使用者希望Finchley版的Spring Cloud Gateway上使用OpenTracing,需要做如下改造
<dependency>
    <groupId>com.nepxion</groupId>
    <artifactId>discovery-plugin-strategy-starter-gateway</artifactId>
    <version>${discovery.version}</version>
    <exclusions>
        <exclusion>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-core</artifactId>
    <version>3.2.3.RELEASE</version>
</dependency>

上述方式也适用于其它引入了低版本reactor-core包版本兼容性的场景

相关下载

DiscoveryAgent下载

访问https://github.com/Nepxion/DiscoveryAgent/releases获取最新版本

DiscoveryDesktop下载

访问https://github.com/Nepxion/DiscoveryUI/releases获取最新版本