6.2.0(SEP 20, 2020)
发布日志
- 本次发布版本,深入和
Spring Cloud Alibaba
、Nacos
团队进行探讨、合作、测试,并结合若干家公司的落地实践,进行优化和重构,以更强大的功能,解决使用者的真正痛点,以更开放的方式,供使用者灵活扩展 - 欢迎使用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 |
|
G | 2.1.x | 2.1.x | ||
F | 2.0.x | 2.0.x | ||
3.19.0 | E | 1.5.x | 1.5.x | |
D | 1.x.x | N/A | ||
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 - 不再需要在启动参数里配置
-D
thread.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
获取最新版本