Nacos配置中心客户端处理服务端配置信息源码解析

文章目录


前言

Nacos配置中心,对于页面上用户操作的配置信息,实际上是使用了推拉结合的模式。

  • 服务端将配置信息推送到客户端。
  • 客户端主动向服务端发起请求拉取配置信息。

本篇介绍服务端将配置信息推送到客户端,客户端的处理方式

一、服务端推送配置信息,客户端的处理

1.1、客户端获取服务端请求,发布事件

当用户在页面上对配置信息进行操作时,会调用服务端ConfigControllerpublishConfig。最终还会将配置信息推送到客户端:

该请求会被客户端的ClientWorker.ConfigRpcTransportClient#initRpcClientHandler接收:

分为了两步操作:

  1. RpcClientserverRequestHandlers集合中注册一个Handler。
  1. 实例化该Handler的对象,在合适的时机调用。

在实例化ServerRequestHandler对象,重写的requestReply方法中,先是通过组合dataIdgrouptenant 生成一个唯一标识配置项的groupKey,然后去缓存中查找,如果缓存中存在对应配置:

  • 同步锁定该CacheData实例;
  • 更新最后修改时间戳;
  • 标记客户端缓存与服务端不同步;
  • 触发监听器通知。

CacheData是Nacos客户端承载配置信息的对象,存放了基本信息:

notifyListenConfig方法中,是向listenExecutebell队列中放入了一个空的Object对象,而非具体的事件**


1.2、客户端启动时,轮询监听事件

listenExecutebell队列中的元素何时被消费?

spring-cloud-starter-alibaba-nacos-config的jar包中的spring.factories文件中,有NacosConfigBootstrapConfiguration类,是Nacos配置中心启动 的相关配置。
  向Spring容器中条件装配了三个Bean。其中和本文中功能相关的,是NacosConfigManager(Nacos配置管理者)。

NacosConfigManager的构造中,实际上是要给NacosConfigManager类的两个属性赋值。

  • nacosConfigProperties代表了配置文件中的信息。
  • service是配置管理 的核心接口,定义了客户端与配置中心交互的主要 API,例如获取配置、发布配置、监听配置变更等操作。这里赋值给它的是其子类NacosConfigService

尝试通过反射创建NacosConfigService的实例。(通过NacosConfigService的有参构造,传入properties参数)

又会去创建ClientWorker的实例:

创建ClientWorker的实例的关键代码:

  • 实例化了ConfigRpcTransportClient,它是ClientWorker的一个内部类。
  • 调用ConfigRpcTransportClientstart方法。

start方法,最终会调用到ConfigRpcTransportClientstartInternal,同样是在线程池中开启了一个死循环,当线程池存活时,就会一直执行里面的逻辑。**这里的listenExecutebell就是notifyListenConfig方法中放入事件的那个阻塞队列,每隔5s从队列中获取元素,获取不到就会陷入阻塞。

这里获取到的不是具体的事件,而是一个空的Object对象。这个 Object 实际是一个"唤醒标记",而不是用来传递事件数据的。(设计精髓)


上面的NacosConfigBootstrapConfiguration类。是在SpringBoot整合了Nacos,启动时利用自动配置机制完成的。所以这一块的设计和Nacos注册中心服务端获取客户端的注册信息、以及配置中心服务端消费者消费事件,向客户端发送通知 这两者的设计,是同样的道理。都是在程序启动的过程中,先初始化一个线程去轮询监听 + 阻塞队列。在相应的请求到达时,从队列中获取元素进行处理。

1.3、客户端订阅事件并处理

当从listenExecutebell中获取到标记后,就会进入executeConfigListen的逻辑,真正地去进行事件的处理,关键代码在于refreshContentAndCheck

在该方法中,首先会从缓存配置信息的Map中,获取该groupKey对应的缓存信息,然后再次调用重载的refreshContentAndCheck

  1. 远程获取配置文件
  2. 对有变化的配置调用对应的监听器去处理

1.3.1、getServerConfig远程获取配置文件

最终调用到的是queryConfig方法:

  1. 又会去向服务端发送请求,获取最新的配置。
  2. 保存到本地(快照)。

服务端接收该请求的是ConfigQueryRequestHandlerhandle方法:

最终是从磁盘 获取的配置文件,然后响应给客户端的,所以直接改数据库中的配置信息,客户端是感知不到的。

这里为什么要从磁盘获取?服务端不是保存了一份到数据库中吗?

可能是考虑到性能问题,从磁盘获取文件的效率,要高于数据库的IO操作(省去了连接,响应的时间)。

1.3.2、更新文件变更checkListenerMd5

最终会来到safeNotifyListener,在其中,有两处关键的代码:

利用线程池提交一个job

job中利用listenerreceiveConfigInfo方法,真正地执行逻辑

选择AbstractSharedListener的实现:

实际调用的是子类NacosContextRefresher重写的innerReceive方法:

  1. 添加一条配置信息的历史记录(在本地记录一个配置变更历史,并控制记录列表的大小不超过阈值。)
  2. 发布配置信息的刷新事件 ,对应的监听器是RefreshEventListener

这里向客户端的内存中保存了一份,如何保证和数据库中历史记录的一致性?

客户端历史记录仅用于本地用途,不参与服务端页面上的历史记录,页面展示的历史记录来源于服务端数据库;

刷新事件与Nacos的动态刷新配置有关。

总结:Nacos 配置中心客户端处理服务端推送配置信息的流程解析

在 Nacos 配置中心中,服务端 在检测到配置内容发生变更后,会客户端推送变更通知。客户端收到推送通知,并不会直接处理变更配置内容,而是通过一系列异步机制完成完整的配置拉取与刷新流程。


客户端接收服务端推送通知

当服务端发起配置变更的推送请求时,客户端内部的 ClientWorker.ConfigRpcTransportClient#initRpcClientHandler 会接收到该请求。客户端在接收到该通知后,并不会立即处理配置内容,而是向内部的阻塞队列 listenExecutebell 中放入一个空的 Object 实例,作为"配置已变更"的标记。

这一设计的核心在于将处理逻辑解耦:推送通知的接收与配置内容的处理并不在同一线程中完成,而是通过队列和监听线程实现异步化与任务排队。


客户端监听线程的启动机制

在客户端启动过程中,Nacos 通过 Spring Boot 的自动配置机制,在 NacosConfigBootstrapConfiguration 中初始化配置环境。该类会创建用于监听配置变化的线程池,启动监听任务。

该监听线程会轮询检查 listenExecutebell 队列,一旦检测到有新元素被放入(即接收到变更标记),便开始配置处理流程。


配置变更处理逻辑

监听线程在检测到配置变更标记后,分两个步骤完成处理:

  1. 远程拉取最新配置内容并保存快照
    客户端会通过远程调用向服务端发起配置查询请求,对应服务端处理类为 ConfigQueryRequestHandler#handle。服务端接收到请求后,会从磁盘读取对应的配置信息,并将结果返回给客户端。客户端收到后,会将配置内容写入本地快照中,用于容灾和本地缓存读取。
  2. 比对并触发配置变更事件
    客户端计算拉取到的配置内容的 MD5,与缓存中的 MD5 进行比对。如果内容发生变化,则会:
    • 记录一条配置变更历史(仅保存在客户端内存中);
    • 触发配置刷新事件,发布到 Spring 上下文中,由 RefreshEventListener 等监听器进行处理。

需要注意的是:客户端本地记录的配置历史仅供本地诊断与调试使用,不会同步到服务端,也不参与页面展示的历史版本信息 。Nacos 控制台页面中展示的历史记录,来源于服务端数据库的 his_config_info 表,由服务端在每次配置发布时自动写入。


架构设计理念

这种设计体现了 Nacos 配置中心架构的核心理念:事件驱动 + 队列解耦 + 监听异步处理 。同时体现 轻推重拉(轻量推送、客户端主动拉取) 的策略

服务端推送的不是配置内容 ,而是变更事件。这个事件就是一个带有定位信息的"标记",客户端基于这个事件再去主动获取配置内容,实现了高效、实时、低耦合的配置同步机制。客户端并不会直接处理服务端推送的配置信息,而是通过监听线程加阻塞队列的方式,将推送转化为本地的"变更信号",再由客户端主动拉取配置、判断差异、触发刷新事件。

字段 说明
dataId 变更的配置项 ID
group 配置所属的分组
tenant 租户信息(多租户场景)

这种机制与 Nacos 注册中心中的客户端注册/订阅模型具有高度相似性:

  • 程序启动时初始化监听线程;
  • 使用阻塞队列作为"信号触发器";
  • 接收到变更后进行数据拉取和后续处理。

  • 客户端不会直接使用服务端推送的配置内容,而是收到通知后主动发起查询;
  • 推送仅作为"配置已变更"的标记,通过阻塞队列传递到监听线程;
  • 监听线程完成实际配置拉取、MD5 校验、历史记录记录以及刷新事件发布;
  • 客户端历史记录仅用于本地用途,控制台页面展示的历史版本来源于服务端数据库;
  • 整体设计体现了事件驱动、异步解耦的系统架构思想。
相关推荐
MaCa .BaKa1 分钟前
33-公交车司机管理系统
java·vue.js·spring boot·maven
神仙别闹6 分钟前
基于C++(MFC)的细胞识别程序
开发语言·c++·mfc
Tiger Z10 分钟前
R 语言科研绘图 --- 饼状图-汇总
开发语言·人工智能·程序人生·r语言·贴图
神仙别闹11 分钟前
基于C++(MFC)图形编辑界面工具
开发语言·c++·mfc
和尚用0飘柔012 分钟前
【中间件】nginx将请求负载均衡转发给网关,网关再将请求转发给对应服务
nginx·中间件·负载均衡
晓龙的Coding之路18 分钟前
python生成项目依赖文件requirements.txt
linux·开发语言·python
洛小豆27 分钟前
一个场景搞明白Reachability Fence,它就像一道“结账前别走”的红外感应门
java·后端·面试
500佰29 分钟前
AI提示词(Prompt)设计优化方案 | 高效使用 AI 工具
java·人工智能·prompt·ai编程
摘星编程31 分钟前
并发设计模式实战系列(4):线程池
java·设计模式·并发编程
无影无踪的青蛙33 分钟前
[Python] 递推(讲解 + 刷题)
开发语言·python