Nacos 配置中心源码 | 京东物流技术团队

京东云开发者
• 阅读 322

客户端

入口

在引入配置中心 maven 依赖的 jar 文件中找到 spring-cloud-starter-alibaba-nacos-config-2.2.5.RELEASE.jar!/META-INF/spring.factories,在该配置文件找到 NacosConfigBootstrapConfiguration 配置类,该类是 nacos 配置中心的入口类,类中注册了三个 bean。

Nacos 配置中心源码 | 京东物流技术团队

Nacos 配置中心源码 | 京东物流技术团队

NacosConfigProperties:属性配置类,对应配置文件中 spring.cloud.nacos.config 前缀的属性。

NacosConfigManager:管理 NacosConfigProperties 和 ConfigService。

NacosPropertySourceLocator:加载配置中心配置信息。

NacosConfigManager

在 NacosConfigManager 构造方法中,调用了 createConfigService 方法,该方法通过工厂类调用 ConfigService 实现类的构造方法创建 ConfigService 实例。

Nacos 配置中心源码 | 京东物流技术团队

Nacos 配置中心源码 | 京东物流技术团队

在 ConfigService 的实现类 NacosConfigService 的构造方法中会初始化 this.agent = new MetricsHttpAgent(new ServerHttpAgent(properties));,该 agent 是用来像服务端发送请求的代理。

Nacos 配置中心源码 | 京东物流技术团队

Nacos 配置中心源码 | 京东物流技术团队

ServerHttpAgent 类中 NacosRestTemplate 属性是发送远程调用的工具类,会调用 HttpMethod.GET 方法调用服务端 rest 请求。

Nacos 配置中心源码 | 京东物流技术团队

在回到 NacosConfigService#NacosConfigService 的方法中 this.worker = new ClientWorker(this.agent, this.configFilterChainManager, properties); 该属性是客户端工作线程类,在类的内部有两个线程池:

1. 只有一个线程的线程池 this.executor = Executors.newScheduledThreadPool(1, new ThreadFactory() 用来执行定时任务,每隔 10ms 执行一次 checkConfigInfo(); 方法,按照每 3000 个配置项为一批次捞取待轮询的 cacheData 实例,将其包装成为一个 LongPollingTask 提交进入第二个线程池 executorService 处理。

Nacos 配置中心源码 | 京东物流技术团队

Nacos 配置中心源码 | 京东物流技术团队

2.线程数等于处理器个数的线程池,用来执行 ClientWorker.LongPollingRunnable#LongPollingRunnable#run,cacheMap 中缓存着需要刷新的配置,将 cacheMap 中数量以 3000 分一个组,分别创建一个 LongPollingRunnable 用来监听配置更新,在 LongPollingRunnable#run 方法中调用checkLocalConfig(cacheData); 检查本地的配置,容错的处理;调用 checkUpdateDataIds(cacheDatas, inInitializingCacheList); 方法是向 nacos 服务端 发送一个长连接超时事件30s,返回有更新的dataids;调用 getServerConfig(dataId, group, tenant, 3000L); 方法是根据返回有变化的dataids调用服务端配置中心接口获取配置属性,并更新本地快照;调用checkListenerMd5();方式,对有变化的配置添加监听处理;最后继续调用executorService.execute(this); 方法轮询处理。

Nacos 配置中心源码 | 京东物流技术团队

Nacos 配置中心源码 | 京东物流技术团队

Nacos 配置中心源码 | 京东物流技术团队

CacheData#checkListenerMd5

Nacos 配置中心源码 | 京东物流技术团队

Nacos 配置中心源码 | 京东物流技术团队

在listener.receiveConfigInfo(contentTmp); 方法中会调用到 AbstractSharedListener#receiveConfigInfo 方法,会发布 RefreshEvent 事件。

Nacos 配置中心源码 | 京东物流技术团队

Nacos 配置中心源码 | 京东物流技术团队

对应的事件监听器为:RefreshEventListener, Spring Cloud 实现的,在该监听器里更新配置和刷新容器中标记了 @RefreshScope 的配置,在 onApplicationEvent 方法中监听2个事件,ApplicationReadyEvent(spring boot 事件,表示 application 应该初始化完成)、RefreshEvent。

Nacos 配置中心源码 | 京东物流技术团队

Nacos 配置中心源码 | 京东物流技术团队

RefreshEvent:this.handle((RefreshEvent)event);处理该事件,用来刷新容器中标记了 @RefreshScope注解的配置,org.springframework.cloud.context.refresh.ContextRefresher#refresh

Nacos 配置中心源码 | 京东物流技术团队

Nacos 配置中心源码 | 京东物流技术团队

refreshEnvironment();中 extract(this.context.getEnvironment().getPropertySources()) 抽取除系统变量外的其他变量;addConfigFilesToEnvironment();把原有的 environment里面的参数放到一个新建的 spring context 容器下重新加载,完事之后关闭新容器,这里就是获取参数的新值;

Nacos 配置中心源码 | 京东物流技术团队

Nacos 配置中心源码 | 京东物流技术团队

changes(before,extract(this.context.getEnvironment().getPropertySources())) 获取新的参数值,并和之前得进行比较找出改变得参数值。

Nacos 配置中心源码 | 京东物流技术团队

this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys)); 发布环境变更事件,并带上改变得参数值。

回到 ContextRefresher#refresh 方法,看下 this.scope.refreshAll(); 刷新标记@RefreshScope注解的 bean。

Nacos 配置中心源码 | 京东物流技术团队

Nacos 配置中心源码 | 京东物流技术团队

super.destroy(); 方法,清楚 scope 里面的缓存,下次就会重新从 BeanFactory 获取一个新的实例会使用新的配置。

this.context.publishEvent(new RefreshScopeRefreshedEvent()); 方法发布事件。

服务端

DumpService

DumpService 类是一个抽象类负责从存储中查询配置保存到磁盘上,它有两个子类,EmbeddedDumpService嵌入式存储(DERBY)、ExternalDumpService扩展数存储。

Nacos 配置中心源码 | 京东物流技术团队

ExternalDumpService 实现类的 init 方法上 @PostConstruct 注解,在 spring 构建 bean 的过程中会执行带有 @PostConstruct 的初始化方法。

Nacos 配置中心源码 | 京东物流技术团队

调用到抽象父类 DumpService#dumpOperate 的方法,调用到 dumpConfigInfo 方法,dumpConfigInfo 方法会判断是全量更新,还是追加更新。

Nacos 配置中心源码 | 京东物流技术团队

如果 isAllDump 为true 会走全量更新,会进行判断是否有快速更新配置、是否存在心跳检查文件、最后检查时间是否小于 6小时,上述判断都满足就不走全量更新,否则走全量更新。

Nacos 配置中心源码 | 京东物流技术团队

dumpAllProcessor.process(new DumpAllTask());将数据库中的所有 configInfo 配置信息查询出来,写入服务器端磁盘缓存。

Nacos 配置中心源码 | 京东物流技术团队

persistService.findConfigMaxId(); 查询数据库中最大的主键,用于分页处理。

Nacos 配置中心源码 | 京东物流技术团队

persistService.findAllConfigInfoFragment(lastMaxId, PAGE_SIZE); 从数据库中分页查询数据,每次查询1000条。

Nacos 配置中心源码 | 京东物流技术团队

Nacos 配置中心源码 | 京东物流技术团队

ConfigCacheService.dump(cf.getDataId(), cf.getGroup(), cf.getTenant(), cf.getContent(), cf.getLastModified(),cf.getType()); 写入磁盘

Nacos 配置中心源码 | 京东物流技术团队

保存到文件中

Nacos 配置中心源码 | 京东物流技术团队

updateMd5(groupKey, md5, lastModifiedTs); 缓存配置信息的 MD5 到内存中,并发布 LocalDataChangeEvent 事件。

Nacos 配置中心源码 | 京东物流技术团队

事件监听器会在 NotifyCenter.registerSubscriber 调用。

Nacos 配置中心源码 | 京东物流技术团队

获取配置

HttpMethod.GET /nacos/v1/cs/configs 获取服务端配置接口,ConfigController#getConfig。

Nacos 配置中心源码 | 京东物流技术团队

在getConfig 中调用了 inner.doGetConfig(request, response, dataId, group, tenant, tag, clientIp);

在 doGetConfig 方法中会调用 DiskUtil.targetBetaFile(dataId, group, tenant);方法,从本地磁盘上获取,不是从 mysql 中拉取,如果直接修改 mysql数据不会生效的,需要发布 ConfigDataChangeEvent 事件,触发更新。

Nacos 配置中心源码 | 京东物流技术团队

监听配置

HttpMethod.POST 请求调用 /nacos/v1/cs/configs/listener 轮询接口调用长连接。

Nacos 配置中心源码 | 京东物流技术团队

longPollingService.addLongPollingClient(request, response, clientMd5Map, probeRequestSize); 长连接轮询处理。

Nacos 配置中心源码 | 京东物流技术团队

SwitchService.getSwitchInteger(SwitchService.FIXED_DELAY_TIME, 500); 最多处理29.5s 需要保留0.5s来响应客户端,避免超时。

Nacos 配置中心源码 | 京东物流技术团队

MD5Util.compareMd5(req, rsp, clientMd5Map); 比较客户端的 md5 与当前服务端的是否一致,不一致返回到 changedGroups。

Nacos 配置中心源码 | 京东物流技术团队

有不一致数据直接响应 generateResponse(req, rsp, changedGroups);

Nacos 配置中心源码 | 京东物流技术团队

线程池执行长连接任务 ConfigExecutor.executeLongPolling。

Nacos 配置中心源码 | 京东物流技术团队

LongPollingService.ClientLongPolling#run 长轮询。

Nacos 配置中心源码 | 京东物流技术团队

ConfigExecutor.scheduleLongPolling 延迟 29.5s 执行

Nacos 配置中心源码 | 京东物流技术团队

延迟执行先删除队列中自己的任务 allSubs.remove(ClientLongPolling.this);

Nacos 配置中心源码 | 京东物流技术团队

allSubs.add(this); 添加到队列

Nacos 配置中心源码 | 京东物流技术团队

inner.doPollingConfig(request, response, clientMd5Map, probeModify.length());

Nacos 配置中心源码 | 京东物流技术团队

MD5Util.compareMd5(request, response, clientMd5Map); 和当前配置比较,返回有变更的配置

nacos 管理端变更配置

HttpMethod.POST /nacos/v1/cs/configs

Nacos 配置中心源码 | 京东物流技术团队

persistService.insertOrUpdate(srcIp, srcUser, configInfo, time, configAdvanceInfo, true); 持节化信息到数据库。

Nacos 配置中心源码 | 京东物流技术团队

Nacos 配置中心源码 | 京东物流技术团队

Nacos 配置中心源码 | 京东物流技术团队

Nacos 配置中心源码 | 京东物流技术团队

回到 ConfigController#publishConfig 看下 ConfigChangePublisher.notifyConfigChange 方法,触发 ConfigDataChangeEvent 事件。

Nacos 配置中心源码 | 京东物流技术团队

Nacos 配置中心源码 | 京东物流技术团队

ConfigDataChangeEvent 事件监听。

Nacos 配置中心源码 | 京东物流技术团队

ConfigExecutor.executeAsyncNotify(new AsyncTask(nacosAsyncRestTemplate, queue)); 同步其他节点。

Nacos 配置中心源码 | 京东物流技术团队

还有 LongPollingService 初始化的时候订阅了 LocalDataChangeEvent 事件,也会监听到。

Nacos 配置中心源码 | 京东物流技术团队

ConfigExecutor.executeLongPolling(new DataChangeTask(evt.groupKey, evt.isBeta, evt.betaIps));

看下 LongPollingService.DataChangeTask#run,push 模式, 遍历 allSubs 把变化的 key 响应客户端。clientSub.sendResponse(Arrays.asList(groupKey));

Nacos 配置中心源码 | 京东物流技术团队

作者:京东物流 张士欣

来源:京东云开发者社区 自猿其说Tech 转载请注明来源

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Stella981 Stella981
3年前
Nacos配置中心动态获取数组配置
有的时候我们需要动态获取一系列的配置项,假设我们在nacos配置中心的配置如下,nacos配置中心的其他设置请参考Nacos搭建流程(https://my.oschina.net/u/3768341/blog/3138297)skill:name:爆炸冲刺在SpringCloud代
Wesley13 Wesley13
3年前
VBox 启动虚拟机失败
在Vbox(5.0.8版本)启动Ubuntu的虚拟机时,遇到错误信息:NtCreateFile(\\Device\\VBoxDrvStub)failed:0xc000000034STATUS\_OBJECT\_NAME\_NOT\_FOUND(0retries) (rc101)Makesurethekern
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
ELK学习笔记之配置logstash消费kafka多个topic并分别生成索引
0x00 filebeat配置多个topicfilebeat.prospectors:input_type:logencoding:GB2312fields_under_root:truefields:添加字段
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_