Nacos配置中心的几个核心概念:dataId、group、namespace,它们的层级关系如下图:
dataId:是配置中心里最基础的单元,它是一种key-value结构,key通常是我们的配置文件名称,比如:application.yml、mybatis.xml,而value是整个文件下的内容。
目前支持JSON、XML、YAML等多种配置格式。
group:dataId配置的分组管理,比如同在dev环境下开发,但同环境不同分支需要不同的配置数据,这时就可以用分组隔离,默认分组DEFAULT_GROUP。
namespace:项目开发过程中肯定会有dev、test、pro等多个不同环境,namespace则是对不同环境进行隔离,默认所有配置都在public里。
架构设计下图简要描述了nacos配置中心的架构流程。
客户端、控制台通过发送Http请求将配置数据注册到服务端,服务端持久化数据到Mysql。
客户端拉取配置数据,并批量设置对dataId的监听发起长轮询请求,如服务端配置项变更立即响应请求,如无数据变更则将请求挂起一段时间,直到达到超时时间。为减少对服务端压力以及保证配置中心可用性,拉取到配置数据客户端会保存一份快照在本地文件中,优先读取。
这里我省略了比较多的细节,如鉴权、负载均衡、高可用方面的设计(其实这部分才是真正值得学的,后边另出文讲吧),主要弄清客户端与服务端的数据交互模式。
下边我们以Nacos 2.0.1版本源码分析,2.0以后的版本改动较多,和网上的很多资料略有些不同
地址:https://github.com/alibaba/nacos/releases/tag/2.0.1
Nacos配置中心的客户端源码在nacos-client项目,其中NacosConfigService实现类是所有操作的核心入口。
说之前先了解个客户端数据结构cacheMap,这里大家重点记住它,因为它几乎贯穿了Nacos客户端的所有操作,由于存在多线程场景为保证数据一致性,cacheMap采用了AtomicReference原子变量实现。
/** * groupKey -> cacheData. */ private final AtomicReference<Map<String, CacheData>> cacheMap = new AtomicReference<Map<String, CacheData>>(new HashMap<>());cacheMap是个Map结构,key为groupKey,是由dataId, group, tenant(租户)拼接的字符串;value为CacheData对象,每个dataId都会持有一个CacheData对象。
获取配置
Nacos获取配置数据的逻辑比较简单,先取本地快照文件中的配置,如果本地文件不存在或者内容为空,则再通过HTTP请求从远端拉取对应dataId配置数据,并保存到本地快照中,请求默认重试3次,超时时间3s。
获取配置有getConfig()和getConfigAndSignListener()这两个接口,但getConfig()只是发送普通的HTTP请求,而getConfigAndSignListener()则多了发起长轮询和对dataId数据变更注册监听的操作addTenantListenersWithContent()。
@Override public String getConfig(String dataId, String group, long timeoutMs) throws NacosException { return getConfigInner(namespace, dataId, group, timeoutMs); } @Override public String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener) throws NacosException { String content = getConfig(dataId, group, timeoutMs); worker.addTenantListenersWithContent(dataId, group, content, Arrays.asList(listener)); return content; }注册监听
客户端注册监听,先从cacheMap中拿到dataId对应的CacheData对象。
public void addTenantListenersWithContent(String dataId, String group, String content, List<? extends Listener> listeners) throws NacosException { group = blank2defaultGroup(group); String tenant = agent.getTenant(); // 1、获取dataId对应的CacheData,如没有则向服务端发起长轮询请求获取配置 CacheData cache = addCacheDataIfAbsent(dataId, group, tenant); synchronized (cache) { // 2、注册对dataId的数据变更监听 cache.setContent(content); for (Listener listener : listeners) { cache.addListener(listener); } cache.setSyncWithServer(false); agent.notifyListenConfig(); } }如没有则向服务端发起长轮询请求获取配置,默认的Timeout时间为30s,并把返回的配置数据回填至CacheData对象的content字段,同时用content生成MD5值;再通过addListener()注册监听器。