当注册中心变更或遇到突发情况时,可能导致订阅列表异常从而收到空的实例列表。推空保护功能可以有效保护业务调用,增加业务可靠性。本文介绍如何在客户端和服务端开启推空保护。

前提条件

背景信息

服务消费者(Consumer)会从注册中心上订阅服务提供者(Provider)的实例列表。没有任何服务百分百可用,注册中心在进行变更(变配、升降级)或遇到突发情况(例如可用区断网断电)时,都有可能出现订阅异常,导致服务消费者获取到空的服务提供者的实例列表,继而影响服务消费者的可用性。

为应对订阅列表不可预知的异常情况,您可以开启推空保护的功能,提高整体系统的可用性。

客户端的推空保护

使用限制

Nacos Java Client 1.4.1及以上版本支持客户端推空保护功能。

为避免使用有风险的版本,请参见版本推荐MSE Nacos SDK的应用和Nacos SDK限制使用版本为客户端、Spring Cloud和Dubbo选择合适的版本。

开启推空保护

  • 若您使用的是Java版本的Nacos-Client。

    引入nacos-client依赖:

    <!-- ${nacos-client.version} 需为1.4.1及以上版本 -->
    <dependency>
        <groupId>com.alibaba.nacos</groupId>
        <artifactId>nacos-client</artifactId>
        <version>${nacos-client.version}</version>
    </dependency>

    请在程序代码中按照下列方式配置使用:

    Properties properties = new Properties();
    properties.put(PropertyKeyConst.SERVER_ADDR, "${mseNacos实例域名}");
    properties.put(PropertyKeyConst.NAMING_PUSH_EMPTY_PROTECTION, "true");
    NamingService naming = NamingFactory.createNamingService(properties);
  • 若您使用的是Spring Cloud Alibaba框架,请在应用配置文件添加下列配置。
    spring.cloud.nacos.discovery.namingPushEmptyProtection=true
  • 若您使用的是Dubbo框架,请在Dubbo配置文件的注册中心链接URL中添加下列配置。
    dubbo.registry.address=nacos://${mseNacos实例域名}:8848?namingPushEmptyProtection=true

服务端的推空保护

客户端的推空保护目前仅支持使用Java语言新版本的客户端。由于目前仍有大量多语言的客户端以及使用旧版本的Java客户端,MSE在开源Nacos的基础上增强了服务端能力,提供服务端的推空保护能力。

使用限制

Nacos引擎版本需升级至2.1.0.0及以上。具体操作,请参见升级引擎版本

开启推空保护

Nacos引擎的版本不同,开启推空保护的方式也有所不同。

  • 若您的MSE Nacos引擎版本为2.1.0.0或2.1.0.1,无需任何操作,对应版本会自动开启推空保护。
  • 若您的MSE Nacos引擎版本为2.1.0.2及以上,需要通过以下操作开启推空保护。
    1. 登录MSE注册配置中心管理控制台,并在顶部菜单栏选择地域。
    2. 在左侧导航栏,选择注册配置中心 > 实例列表
    3. 实例列表页面,单击目标实例名称。
    4. 在实例基础信息的左侧导航栏,单击参数设置,然后在参数设置页面的运行时生效参数区域,单击forcePushEmptyProtectionForAllService右侧操作列下方的编辑
    5. 参数修改对话框,选中参数值右侧的,然后单击确定
      说明 为保证业务的最高可用性,建议所有情况下均开启推空保护,即forcePushEmptyProtectionForAllService参数值均选择

结果验证

当有服务触发推空保护时,您可以在服务消费者(Consumer)所处节点上的${user.home}/logs/nacos/naming.log日志中看到Trigger push empty protection for Service字样。

正常情况下,该信息不会频繁出现,如果频繁出现,可根据以下情况进行排查和治理。

  • 订阅不存在的服务。

    如果应用订阅不存在的服务,需要排查该应用是否仍需要该依赖关系。如不需要,删除该无效依赖;如仍然需要,需重新发布上线该服务的提供者。

  • 应用订阅的服务提供者为空。
    • 查看该服务的提供者是否频繁掉线。
    • 查看该服务的提供者是否与MSE Nacos集群的网络连接不健康,导致Nacos认为此服务的提供者已经下线。

定位触发推空保护的服务

错误信息中包含触发推空保护的服务Service{namespace='XXX'、group='xx'、name='xxxxxxx'、ephemeral=true、revision=0},由此可以得知服务消费者已订阅哪个空服务。

已知容易触发推空保护的主要场景

  • Dubbo 2的兼容多订阅场景下,旧格式服务名为空。

    触发原因:Dubbo 2.7旧版本(2.7.6之前的版本)所注册的服务名格式与Dubbo 2.7新版本的服务名格式不同。Dubbo 2.7新版本使用多订阅的场景,即同时订阅旧格式和新格式的服务名。当所有提供者均为新版本时,旧格式的服务在Nacos中必定不存在,导致订阅旧格式时触发推空保护。

    解决方式:升级引擎至2.1.0.1以上版本,或升级Dubbo至2.7.17以上。具体操作,请参见升级引擎版本

  • Dubbo 3的兼容多订阅场景下,接口级服务为空。

    触发原因:Dubbo 3支持应用级服务发现,所注册的服务名不再是接口名而是应用名。为了支持旧版本平滑升级,同时订阅应用级和接口级的服务名。当所有提供者均为Dubbo 3版本时,接口级服务必定不存在,因此订阅接口级服务时会触发推空保护。

    解决方式:设置Dubbo配置dubbo.application.service-discovery.migration=FORCE_APPLICATION。具体操作,请参见Dubbo官网

  • Spring Cloud首次启动NacosWatch功能且没有其他实例副本的场景下,服务为空。

    触发原因:Spring Cloud Alibaba在新版本中新增功能NacosWatch,用于监听自身服务状态,该功能会在启动应用时监听自身服务。当该功能首次启动且没有其他实例副本时,由于注册中心没有该服务,订阅会触发推空保护,应用启动完成后恢复。

    解决方式:首次启动且无其他副本时忽略错误,或设置spring.cloud.nacos.discovery.watch.enabled=false关闭Watch功能,具体操作,请参见Spring Cloud Alibaba

  • Spring Cloud Gateway完全下线某服务场景下,服务状态未同步。

    触发原因:Spring Cloud Gateway会在启动时查询注册中心中目前所有的服务列表,并订阅所有服务。例如,某个服务在Gateway启动后彻底下线(全部实例移除),是因为注册中心自动摘除了下线服务,但Spring Cloud Gateway不感知,仍然继续订阅,导致触发推空保护。

    解决方式:目前Spring Cloud Gateway不支持动态感知某个服务完全移除后取消订阅,建议您重启Spring Cloud Gateway。

强制进行推空保护的场景

MSE Nacos 2.1.0.2及以上版本,引擎对推空保护的逻辑进行了优化,仅在开启forcePushEmptyProtectionForAllService时保护所有空服务列表。但若在以下场景,即使forcePushEmptyProtectionForAllService开关为关闭状态,为保证可用性和稳定性,系统仍然会自动启动推空保护能力。
  • 引擎节点在进行升级、重启或故障自愈时,节点将所有服务自动开启2分钟的推空保护,以保证此期间用户业务可用性。
  • 当服务下所有实例全部移除时,集群将针对该服务自动开启1分钟的推空保护,防止因网络抖动、服务提供者短时间内发生故障等问题导致的异常推空,以保证此场景下用户业务可用性。
说明 为保证业务的最高可用性,建议打开forcePushEmptyProtectionForAllService开关,即在所有情况下均进行推空保护。