基于spring cloud 的灰度发布实践_Nepxion Discovery 6.1.0 生产级版发布

37f3aa1ccc12a091e7436161eb7ce35f.png

发布日志

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

发布策略

63d426edadc7eda7634718581ba2de9d.png 提醒:版本号右边, 表示>=该版本号, 表示<=该版本号e63775b98b8beb8e3005f12da540bcb6.png 提醒:Spring Boot版本和Spring Cloud Alibaba版本需要在版本号后面加上.RELEASE

版本状态SC版本SB版本SCA版本
6.1.08efecd615368ec25e6ec3c72e0c38c3e.pngH.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.004001f5981a25458a37e8b0360c1298c.pngG2.1.x2.1.x
4.15.004001f5981a25458a37e8b0360c1298c.pngF2.0.x2.0.x
3.18.08dc638bf9d805329ee1c55d814aba4e3.pngE1.5.x1.5.x
2.0.x04001f5981a25458a37e8b0360c1298c.pngD1.x.xN/A
1.0.x04001f5981a25458a37e8b0360c1298c.pngC1.x.xN/A

8efecd615368ec25e6ec3c72e0c38c3e.png 表示维护中 | 8dc638bf9d805329ee1c55d814aba4e3.png 表示不维护,但可用,强烈建议升级 | 04001f5981a25458a37e8b0360c1298c.png 表示不维护,不可用,已废弃

  • 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)已废弃

版本变更

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

① 框架变更

  • 默认集成Spring Cloud Hoxton.SR8(可降级)
  • 默认集成Spring Boot到2.3.3.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.version>Hoxton.SR8spring.cloud.version>
    <spring.cloud.alibaba.version>2.2.3.RELEASEspring.cloud.alibaba.version>
    <spring.boot.version>2.3.3.RELEASEspring.boot.version>

    
    

    
    
properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-dependenciesartifactId>
            <version>${spring.cloud.version}version>
            <type>pomtype>
            <scope>importscope>
        dependency>

        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-alibaba-dependenciesartifactId>
            <version>${spring.cloud.alibaba.version}version>
            <type>pomtype>
            <scope>importscope>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-dependenciesartifactId>
            <version>${spring.boot.version}version>
            <type>pomtype>
            <scope>importscope>
        dependency>
    dependencies>
dependencyManagement>

③ 指南示例变更

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

<dependency>
    <groupId>io.opentracing.contribgroupId>
    <artifactId>opentracing-spring-jaeger-starterartifactId>
dependency>

功能迭代

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

df45b457011b6e9f9d170269aa131512.png 该功能在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>grayversion>
    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变更元数据

自行研究

df45b457011b6e9f9d170269aa131512.png 需要注意

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

② 动态元数据变更方式利用第三方注册中心的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 value="e92edde5-0153-4ec8-9cbb-b4d3f415aa33;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 value="192.168.43.101:1201;192.168.*.102;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 parameterServiceEntityList = null;if (parameterEntity != null) {
            Map> 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 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.nepxiongroupId>
    <artifactId>discovery-plugin-strategy-starter-gatewayartifactId>
    <version>${discovery.version}version>
    <exclusions>
        <exclusion>
            <groupId>io.projectreactorgroupId>
            <artifactId>reactor-coreartifactId>
        exclusion>
    exclusions>
dependency>

<dependency>
    <groupId>io.projectreactorgroupId>
    <artifactId>reactor-coreartifactId>
    <version>3.2.3.RELEASEversion>
dependency>

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

相关文档

0f0cb0d060a0e7be8ef5f71307ab6724.png Discovery【探索】微服务企业级解决方案

① Discovery【探索】微服务企业级解决方案文档

  • Discovery【探索】微服务企业级解决方案(PPT版) :http://nepxion.gitee.io/docs/link-doc/discovery-ppt.html
  • Discovery【探索】微服务企业级解决方案(PDF版) :http://nepxion.gitee.io/docs/link-doc/discovery-pdf.html
  • Discovery【探索】微服务企业级解决方案(HTML版) :http://nepxion.gitee.io/docs/link-doc/discovery-html.html

② Discovery【探索】微服务企业级解决方案源码。请访问Gitee镜像获得最佳体验

  • 源码Gitee同步镜像 :https://gitee.com/Nepxion/Discovery
  • 源码Github原镜像 :https://github.com/Nepxion/Discovery

③ Discovery【探索】微服务企业级解决方案指南示例源码。请访问Gitee镜像获得最佳体验

  • 指南Gitee同步镜像 :https://gitee.com/Nepxion/DiscoveryGuide
  • 指南Github原镜像 :https://github.com/Nepxion/DiscoveryGuide

④ Discovery【探索】微服务框架指南示例说明

  • 对于入门级玩家,参考 指南示例极简版 :https://github.com/Nepxion/DiscoveryGuide/tree/simple,分支为simple。涉及到指南篇里的灰度路由和发布的基本功能,e63775b98b8beb8e3005f12da540bcb6.png 参考 新手快速入门 :https://gitee.com/nepxion/DiscoveryGuide/blob/simple/GUIDE.md
  • 对于熟练级玩家,参考 指南示例精进版 :https://github.com/Nepxion/DiscoveryGuide/tree/master,分支为master。除上述《极简版》功能外,涉及到指南篇里的绝大多数高级功能
  • 对于骨灰级玩家,参考 指南示例高级版 :https://github.com/Nepxion/DiscoveryGuide/tree/premium,分支为premium。除上述《精进版》功能外,涉及到指南篇里的ActiveMQ、MongoDB、RabbitMQ、Redis、RocketMQ、MySQL等高级调用链和灰度调用链的整合

e1fa19ef7be087555f0a2c5dc86335b3.png Polaris【北极星】企业级云原生微服务框架

① Polaris【北极星】企业级云原生微服务框架文档

  • Polaris【北极星】企业级云原生微服务框架(PDF版) :http://nepxion.gitee.io/docs/link-doc/polaris-pdf.html
  • Polaris【北极星】企业级云原生微服务框架(HTML版) :http://nepxion.gitee.io/docs/link-doc/polaris-html.html

② Polaris【北极星】企业级云原生微服务框架源码。请访问Gitee镜像获得最佳体验

  • 源码Gitee同步镜像 :https://gitee.com/Nepxion/Polaris
  • 源码Github原镜像 :https://github.com/Nepxion/Polaris

③ Polaris【北极星】企业级云原生微服务框架指南示例源码。请访问Gitee镜像获得最佳体验

  • 指南Gitee同步镜像 :https://gitee.com/Nepxion/PolarisGuide
  • 指南Github原镜像 :https://github.com/Nepxion/PolarisGuide

请联系我

微信、公众号和文档

f8dc259bc577aabef23e4a31c2754319.png31af7db80aeb2e099a7bc3af306d7581.png706bd7d484808c3b79fd9beab4a2888a.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值