1. 本地编译
- fork 源地址:https://github.com/alibaba/nacos.git
- git clone 自己 fork 版本
- 现在基于 1.1.3 版本,nacos 项目基于标签做的版本发布,因此直接 git checkout 1.1.3 然后再基于此新建一个分支,用于源码的修改等
- 在根目录下运行:mvn clean -Dmaven.test.skip=true,下载相关依赖包,检查环境
运行 console 项目主类,加上 VM options:-Dnacos.standalone=true
我们可以在控制台查看 banner 的相关运行信息
2. 修改字符分隔符方便调试
因为报文,使用了特殊的字符,不方便 http 测试请求,因此修改 com.alibaba.nacos.config.server.utils 包下的 MD5Util 类:1
2WORD_SEPARATOR_CHAR = '~'
LINE_SEPARATOR_CHAR = '*'
3. 发布订阅者模式
配置管理的实现,使用了大量的 ScheduledExecutorService 以及事件发布订阅者模式
- 事件分发器/发布者:com.alibaba.nacos.config.server.utils.event.EventDispatcher
- 两个监听者:
AsyncNotifyService 监听 ConfigDataChangeEvent 事件,当配置做更新操作时,进行同步并异步做 http 请求到其它的健康节点
LongPollingService 监听 LocalDataChangeEvent 事件,当配置做更新操作时,一是更新 CacheItem(内存级),二是对当前监听的客户端们做响应 - 两种事件:
ConfigDataChangeEvent:需要节点间同步配置改动的事件
LocalDataChangeEvent:本节点配置改动的事件
4. 数据库访问数据
单机版使用 derby 嵌入式数据库,只能由一个进程访问,查看数据需要停止 Nacos 进程才行,稍微麻烦点,不过 idea 支持 derby 数据查看,会方便点:
数据库的具体信息,从 LocalDataSourceServiceImpl 可以查看,包括数据存放地址等,这是可以通过全局配置来设置的
url:/Users/{user.home}/nacos/data/derby-data
username:nacos
password:空
后台 console 的账号和密码根据配置文件可以查看:console/src/main/resources/META-INF/schema.sql,如果想修改,因为还有一个 Role 表,需要改动 role 和 user 表
5. 配置接口
5.1 配置的监听
客户端监听注册中心的配置,其实本质是根据 groupKey(AppId+groupId+namespace)获取对应的 md5,即配置内容的 md5 进行对比是否有更新。默认等待 30s,即是否在 30s 内有改动。
配置的改动监听有三种场景:A 表示客户端,B表示注册中心
- A->B,其实A的配置很老了,B的配置是最新的,B的监听接口会先根据本节点的 CacheItem 内存的值进行匹配,如果匹配失败,说明A 的值是旧的,会立刻返回给 A groupKey(不是md5或内容),让A去查询最新的配置值(多此一举么~)
- A->B,其实A的配置就是最新的,B的配置和A的配置一样,那么 A 请求阻塞 30s(默认Long-Pulling-Timeout配置),B的监听接口使用 Servlet 3.0 异步进行 10s 一次的循环,来根据 groupKey 拿配置的md5,进行对比是否改动,如果改动则返回,不改动理论上会等待30s。
- A->B,属于情况2的变种,就是,在情况2等待30s过程中,B2配置改变了,那么会根据事件分发以及 http 内部节点之间请求,B2会通知B,B最后来通知A,此时A会立刻返回
5.2 配置的修改
post/put/update/delete 等情况,这里有两个要注意,一是自己节点的内存更新,和其它节点的通知更新
- 自己节点:先存数据库 derby,然后做事件 ConfigDataChangeEvent 的发送,而这个事件会被 AsyncNotifyService 监听到,这个 AsyncNotifyService 会根据节点的健康情况发送给其它的对等节点(B1,B2,B3),这个是通过内部接口 communication 来发送请求的
- 其它节点:得到http请求后,这个communication接口做了两件事,一是 dump:即根据是否是集群模式,如果是集群模式来将配置信息写入到硬盘(TODO:为什么硬盘?不写MYSQL吗?),二是 TaskMgr 0.1s 死轮询关键任务,这个关键任务就包括的事件改动的通知,这个任务内部做了 CacheItem 刷本节点的内存最新值,二是发布 LocalDataChangeEvent 事件给 LongPollingService 监听,其中 LongPollingService 内部会做 DataChangeTask 的任务来让响应 ClientLongPolling.sendResponse() 方法,从而让监听者即时返回 asyncContext.complete()
修改会有点绕,因为里面的发布订阅和定时任务太多了
5.3 配置的删除
- 数据库物理删除
- 发布 ConfigDataChangeEvent 事件
5.4 配置的获取
- 直接获取本节点的 CacheItem 值,这个是 concurrenthashmap 类型,存储在内存中
5.5 配置的整体流程
https://github.com/LiWenGu/nacos.git
https://www.processon.com/view/link/5d441f3fe4b0bc1bbedcf559