从整体上Nacos服务端的配置存储分为三层:
- 内存:Nacos每个节点都在内存里缓存了配置,但是只包含配置的md5(缓存配置文件太多了),所以内存级别的配置只能用于比较配置是否发生了变更,只用于客户端长轮询配置等场景。
- 文件系统:文件系统配置来源于数据库写入的配置。如果是集群启动或mysql单机启动,服务端会以本地文件系统的配置响应客户端查询。
- 数据库:所有写数据都会先写入数据库。只有当以derby数据源(-DembeddedStorage=true)单机(-Dnacos.standalone=true)启动时,客户端的查询配置请求会实时查询derby数据库。
对于写请求,Nacos会将数据先更新到数据库,之后异步写入所有节点的文件系统并更新内存。
CacheItem在Nacos服务端对应一个配置文件,缓存了配置的md5,持有一把读写锁控制访问冲突。
public class CacheItem { // groupKey = namespace + group + dataId final String groupKey; // 配置md5 public volatile String md5 = Constants.NULL; // 更新时间 public volatile long lastModifiedTs; // nacos自己实现的简版读写锁 public SimpleReadWriteLock rwLock = new SimpleReadWriteLock(); // 配置文件类型:text/properties/yaml public String type; // ... 省略其他betaIp和tag相关属性 }
ConfigCacheService一个重要的服务,负责管理所有内存配置CacheItem。
public class ConfigCacheService { // 持久层服务(Derby或MySQL) private static PersistService persistService; // groupKey -> cacheItem. private static final ConcurrentHashMap1.2文件系统CACHE = new ConcurrentHashMap (); // 更新配置的md5 public static void updateMd5(String groupKey, String md5, long lastModifiedTs) { CacheItem cache = makeSure(groupKey); if (cache.md5 == null || !cache.md5.equals(md5)) { cache.md5 = md5; cache.lastModifiedTs = lastModifiedTs; NotifyCenter.publishEvent(new LocalDataChangeEvent(groupKey)); } } // 比较入参md5与缓存md5是否一致 public static boolean isUptodate(String groupKey, String md5, String ip, String tag) { String serverMd5 = ConfigCacheService.getContentMd5(groupKey, ip, tag); return StringUtils.equals(md5, serverMd5); } // 获取缓存配置 public static CacheItem getContentCache(String groupKey) { return CACHE.get(groupKey); } // 创建配置 static CacheItem makeSure(final String groupKey) { CacheItem item = CACHE.get(groupKey); if (null != item) { return item; } CacheItem tmp = new CacheItem(groupKey); item = CACHE.putIfAbsent(groupKey, tmp); return (null == item) ? tmp : item; } // 获取配置读锁 public static int tryReadLock(String groupKey) { CacheItem groupItem = CACHE.get(groupKey); int result = (null == groupItem) ? 0 : (groupItem.rwLock.tryReadLock() ? 1 : -1); return result; } // 释放配置读锁 public static void releaseReadLock(String groupKey) { CacheItem item = CACHE.get(groupKey); if (null != item) { item.rwLock.releaseReadLock(); } } }
Nacos刚启动时,内存中与文件系统中未必存在所有配置,所以DumpService会全量dump配置到文件系统与内存中。 另外当数据库配置发生变化时,也会dump到本地文件系统。
// 启动时DumpService全量dump protected void dumpOperate(DumpProcessor processor, DumpAllProcessor dumpAllProcessor, DumpAllBetaProcessor dumpAllBetaProcessor, DumpAllTagProcessor dumpAllTagProcessor) throws NacosException { // 构造各类runnable任务... // 首次启动,dump数据库中所有配置到文件系统和内存中 dumpConfigInfo(dumpAllProcessor); // 非单机部署,提交dump任务 if (!EnvUtil.getStandaloneMode()) { ConfigExecutor.scheduleConfigTask(heartbeat, 0, 10, TimeUnit.SECONDS); // dump all config ConfigExecutor.scheduleConfigTask(dumpAll, initialDelay, DUMP_ALL_INTERVAL_IN_MINUTE, TimeUnit.MINUTES); // dump beta config ConfigExecutor .scheduleConfigTask(dumpAllBeta, initialDelay, DUMP_ALL_INTERVAL_IN_MINUTE, TimeUnit.MINUTES); // dump tag config ConfigExecutor .scheduleConfigTask(dumpAllTag, initialDelay, DUMP_ALL_INTERVAL_IN_MINUTE, TimeUnit.MINUTES); } ConfigExecutor.scheduleConfigTask(clearConfigHistory, 10, 10, TimeUnit.MINUTES); }1.3数据库
配置文件主要存储在config_info表中。Nacos有三个配置项与数据源的选择有关:
- application.properties中的spring.datasource.platform配置项,默认为空,可以配置为mysql
- -Dnacos.standalone,true代表单机启动,false代表集群启动,默认false
- -DembeddedStorage,true代表使用嵌入式存储derby数据源,false代表不使用derby数据源,默认false
这块感觉比较乱,通过伪代码的方式理一下。主要是spring.datasource.platform在默认为空的场景下,满足条件集群启动且-DembeddedStorage=false(默认false),还是会选择mysql数据源。也就是说,集群启动,如果没特殊配置,Nacos会使用MySQL数据源。
// 指定数据源为mysql,直接返回mysql if (mysql) { return mysql; } // 如果单机部署,没指定数据源,使用derby if (standalone) { return derby; } else { // 如果集群部署且指定使用嵌入式存储,使用derby if (embeddedStorage) { return derby; } else { // 集群部署,默认使用mysql存储 return mysql; } }二、配置查询
GET /v1/cs/configs接口负责配置查询。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)