Nacos配置中心设计分析-客户端
Nacos配置中心设计分析-客户端
- 主要功能
- 客户端初始化
- 重要数据结构
- NacosConfigService
- EventDispatcher
- ServerListManager
- ClientWorker
- CacheData
- 典型场景
- 获取配置文件内容
- 发布配置文件内容
- 添加配置文件监听
- 删除配置文件监听
- 线程模型
- Nacos服务器登录线程
- Nacos服务器列表更新线程
- 客户端工作线程
- 配置文件拉取线程
- 版本
Nacos配置中心按照NameSpace、Group、DataId三级结构来组织配置文件,其中,NameSpace可以用于区分环境,例如dev、test等;Group是服务分组,可以用于标识应用;DataId是配置文件名。在实际应用中,可以将NameSpace和Group与应用绑定,固定写入应用的启动参数。
主要功能
Nacos配置中心客户端主要具有如下能力
- 获取配置文件内容
- 发布配置文件内容
- 添加配置文件监听
- 删除配置文件监听
客户端初始化
- 初始化HTTP代理MetricsHttpAgent:对HttpAgent的进一步封装,增加了统计响应时间的能力,HttpAgent中维护了服务器列表,并启动一个线程不断向服务器发起登录请求,保证客户端的合法性。此外,如果没有指定服务器列表,还会启动一个线程不断查询服务器列表,如果服务器有变动,则发布服务器变更事件,并在本线程调用相应的监听器
- 创建客户端工作线程ClientWorker:定时检查是否添加了需要监控的配置文件,如果有,则把这个文件更新的监控任务分配给拉取线程,不断从服务端拉取最新文件内容,如果发现文件变更,则更新客户端配置文件内容,并调用相应的监听器(如果有指定的线程池则使用该线程池,没有指定则用自身线程执行监听器调用)
初始化代码如下
// NacosConfigServicepublic NacosConfigService(Properties properties) throws NacosException {String encodeTmp = properties.getProperty(PropertyKeyConst.ENCODE);if (StringUtils.isBlank(encodeTmp)) {encode = Constants.ENCODE;} else {encode = encodeTmp.trim();}initNamespace(properties);// 初始化HTTP代理MetricsHttpAgentagent = new MetricsHttpAgent(new ServerHttpAgent(properties));agent.start();// 创建客户端工作线程ClientWorkerworker = new ClientWorker(agent, configFilterChainManager, properties);}
重要数据结构
NacosConfigService
// NacosConfigService
public class NacosConfigService implements ConfigService {......// http代理MetricsHttpAgentprivate HttpAgent agent; // 客户端工作线程private ClientWorker worker;......
}
EventDispatcher
public class EventDispatcher {......// key是事件类型,value是监听器列表static final Map<Class<? extends AbstractEvent>, CopyOnWriteArrayList<AbstractEventListener>> LISTENER_MAP= new HashMap<Class<? extends AbstractEvent>, CopyOnWriteArrayList<AbstractEventListener>>();......
}
ServerListManager
// ServerListManager
public class ServerListManager {......// 服务列表(拼接成一行)private String serverAddrsStr;// 服务器列表(分解serverAddrsStr得到)volatile List<String> serverUrls = new ArrayList<String>();// 服务端点地址(查询服务列表请求地址)public String addressServerUrl;......
}
ClientWorker
// ClientWorker
public class ClientWorker {// 配置文件监听器,key是配置文件的标识,value是CacheDataprivate final AtomicReference<Map<String, CacheData>> cacheMap = new AtomicReference<Map<String, CacheData>>(new HashMap<String, CacheData>());
}
CacheData
// CacheData
public class CacheData {// 配置文件内容private volatile String content;// 监听器列表private final CopyOnWriteArrayList<ManagerListenerWrap> listeners;
}
CacheData是配置文件缓存,应用从CacheData中获取文件内容,CacheData由客户端线程不断拉取更新
典型场景
获取配置文件内容
优先从本地缓存文件读取配置文件内容,失败则从服务器获取,仍然失败,则从历史快照文件获取
// NacosConfigServiceprivate String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException {...// 优先使用本地配置String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant);if (content != null) {......configFilterChainManager.doFilter(null, cr);content = cr.getContent();......return content;}// 本地没有配置文件内容则请求服务端获取try {String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs);cr.setContent(ct[0]);configFilterChainManager.doFilter(null, cr);content = cr.getContent();return content; } catch (NacosException ioe) {if (NacosException.NO_RIGHT == ioe.getErrCode()) {throw ioe;}LOGGER.warn("[{}] [get-config] get from server error, dataId={}, group={}, tenant={}, msg={}",agent.getName(), dataId, group, tenant, ioe.toString());}// 从快照获取LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}, config={}", agent.getName(),dataId, group, tenant, ContentUtils.truncateContent(content));content = LocalConfigInfoProcessor.getSnapshot(agent.getName(), dataId, group, tenant);cr.setContent(content);configFilterChainManager.doFilter(null, cr);content = cr.getContent();return content;}
发布配置文件内容
调用API修改配置文件内容
添加配置文件监听
客户端定时拉取指定文件内容,如果发生变化,更新配置文件内容(存放在cacheMap里),并调用监听器进行相应处理。
增加文件监控代码如下
// ClientWorkerpublic void checkConfigInfo() {// 分任务int listenerSize = cacheMap.get().size();// 向上取整为批数// ParamUtil.getPerTaskConfigSize()为每个线程处理的文件数目,添加文件监听时,会向cacheMap增加一条记录int longingTaskCount = (int) Math.ceil(listenerSize / ParamUtil.getPerTaskConfigSize());if (longingTaskCount > currentLongingTaskCount) {for (int i = (int) currentLongingTaskCount; i < longingTaskCount; i++) {// 要判断任务是否在执行 这块需要好好想想。 任务列表现在是无序的。变化过程可能有问题executorService.execute(new LongPollingRunnable(i));}currentLongingTaskCount = longingTaskCount;}}
拉取文件内容并更新代码如下
// ClientWorkerclass LongPollingRunnable implements Runnable {private int taskId;public LongPollingRunnable(int taskId) {this.taskId = taskId;}@Overridepublic void run() {List<CacheData> cacheDatas = new ArrayList<CacheData>();List<String> inInitializingCacheList = new ArrayList<String>();try {// check failover configfor (CacheData cacheData : cacheMap.get().values()) {if (cacheData.getTaskId() == taskId) {cacheDatas.add(cacheData);try {checkLocalConfig(cacheData);if (cacheData.isUseLocalConfigInfo()) {cacheData.checkListenerMd5();}} catch (Exception e) {LOGGER.error("get local config info error", e);}}}// check server configList<String> changedGroupKeys = checkUpdateDataIds(cacheDatas, inInitializingCacheList);LOGGER.info("get changedGroupKeys:" + changedGroupKeys);for (String groupKey : changedGroupKeys) {String[] key = GroupKey.parseKey(groupKey);String dataId = key[0];String group = key[1];String tenant = null;if (key.length == 3) {tenant = key[2];}try {String[] ct = getServerConfig(dataId, group, tenant, 3000L);CacheData cache = cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant));// 更新配置文件内容cache.setContent(ct[0]);if (null != ct[1]) {cache.setType(ct[1]);}LOGGER.info("[{}] [data-received] dataId={}, group={}, tenant={}, md5={}, content={}, type={}",agent.getName(), dataId, group, tenant, cache.getMd5(),ContentUtils.truncateContent(ct[0]), ct[1]);} catch (NacosException ioe) {String message = String.format("[%s] [get-update] get changed config exception. dataId=%s, group=%s, tenant=%s",agent.getName(), dataId, group, tenant);LOGGER.error(message, ioe);}}for (CacheData cacheData : cacheDatas) {if (!cacheData.isInitializing() || inInitializingCacheList.contains(GroupKey.getKeyTenant(cacheData.dataId, cacheData.group, cacheData.tenant))) {// 调用监听器处理cacheData.checkListenerMd5();cacheData.setInitializing(false);}}inInitializingCacheList.clear();executorService.execute(this);} catch (Throwable e) {// If the rotation training task is abnormal, the next execution time of the task will be punishedLOGGER.error("longPolling error : ", e);executorService.schedule(this, taskPenaltyTime, TimeUnit.MILLISECONDS);}}}
删除配置文件监听
// ClientWorkerpublic void removeListener(String dataId, String group, Listener listener) {group = null2defaultGroup(group);CacheData cache = getCache(dataId, group);if (null != cache) {cache.removeListener(listener);if (cache.getListeners().isEmpty()) {removeCache(dataId, group);}}}
线程模型
Nacos服务器登录线程
- JVM中名称com.alibaba.nacos.client.config.security.updater
- 定时向Nacos服务器发送登录请求,保证客户端的合法性
- 实现类:ServerHttpAgent.匿名类
Nacos服务器列表更新线程
- JVM中名称com.alibaba.nacos.client.Timer
- 定时查询服务器列表(该线程只会在启动参数没有指定服务器列表的时候才会启动),如果有服务器变更,则更新客户端服务器列表信息,并调用该事件的监听器(如果有的话)
- 实现类:ServerListManager.GetServerListTask
客户端工作线程
- JVM中名称com.alibaba.nacos.client.Worker.{agentName}
- 定时检查有没有新订阅的配置文件,如果有则启动一个拉取线程
- 实现类:ClientWorker.匿名类
配置文件拉取线程
- JVM中名称com.alibaba.nacos.client.Worker.longPolling.{agentName}
- 定时拉取服务器配置文件内容,并依次调用该文件的监听器,如果添加监听器时指定了执行线程池,则使用指定线程线程池调用监听器
- 实现类:ClientWorker.LongPollingRunnable
版本
本文基于Nacos的1.2.0版本
Nacos配置中心设计分析-客户端相关推荐
- 阿里面试这样问:Nacos配置中心交互模型是 push 还是 pull ?(原理+源码分析)...
本文来源:公众号「 程序员内点事」 对于Nacos大家应该都不太陌生,出身阿里名声在外,能做动态服务发现.配置管理,非常好用的一个工具.然而这样的技术用的人越多面试被问的概率也就越大,如果只停留在使用 ...
- Nacos配置中心用法详细介绍
上篇文章介绍了 Nacos 作为注册中心的用法,除此之外,Nacos 还能作为配置中心使用,那这篇文章就介绍下 Nacos 作为配置中心的基本用法,首先我们先了解下为什么需要使用配置中心. 一.为什么 ...
- Nacos配置中心介绍
配置中心介绍 1.Spring Cloud Config Spring Cloud Config 为分布式系统的外部配置提供了服务端和客户端的支持方案.在配置的服务端您可以在所有环境中为应用程序管理外 ...
- Linux启动nacos成功日志_微服务系列之Nacos配置中心
Nacos 介绍 Nacos 是 Alibaba 公司推出的开源工具,用于实现分布式系统的服务发现与配置管理.英文全称 Dynamic Naming and Configuration Service ...
- easyconnect获取服务端配置信息失败_图文解析 Nacos 配置中心的实现
本文不会贴太多源码,基本靠图片和文字叙述 全文共 2582 字,预计阅读时间 12 分钟 什么是 Nacos 配置中心的架构 Nacos 使用示例 官方代码示例 Properties 解读 配置项的层 ...
- Alibaba Nacos配置中心功能介绍与不同命名空间、分组等配置
概述:我们前面介绍过 Nacos 可以为我们提供服务注册与发现,以及实现了配置中心功能,本章将介绍nacos 配置中心的使用方法,以及其不同场景下的配置方式.在前面我们介绍过nacos的领域模型(下图 ...
- Nacos配置中心实战,盘古开发框架标配组件
配置中心作为分布式微服务开发的标配组件,业界已有很多成功的典型应用,如:携程 Apollo 分布式配置中心.百度 Disconf 分布式配置中心等.盘古开发框架配置中心基于阿里的 Nacos 提供动态 ...
- hyperf接入阿里云nacos配置中心
nacos官网的介绍 服务(Service)是 Nacos 世界的一等公民.Nacos 支持几乎所有主流类型的"服务"的发现.配置和管理: Nacos 致力于帮助您发现.配置和管理 ...
- 基于 Nacos 配置中心的动态日志配置方案
log4j2 日志的级别不能落 SpringBoot 动态设置 logback 日志的级别 上面这两篇文章只是从技术角度说了,可以实现动态日志配置.但是并没有形成适用生产环境使用的方案.今天介绍一种基 ...
最新文章
- linux 命令常驻,Linux下任务调度的crond常驻命令
- Array和ArrayList区别
- Chromium 操作系统即将支持所有 SBC 单板电脑
- Ext js call方法
- mysql主从复制之异常解决--- Slave_IO_Running: NO
- ES6学习笔记二 新的声明方式和变量的解构赋值!
- 利用caffe的Python接口生成prototxt文件
- nginx 工作原理
- java Doc转Pdf
- matlab mallat算法,小波分解与重构1Mallat算法.PDF
- 征途服务器修改,特色修改之(国家任务篇)
- ubuntu美化--壁纸软件
- ios代码中的内存泄露,内存检测工具leaks 检测不出来
- 面试官最爱问的Redis(三)Redis的基本知识
- 2022年文化艺术品产权交易所研究报告
- oh my zsh 的alias文件
- 常用sql语句(备忘)
- PHP里把括号变成英文的方法,php怎么去除括号
- IDEA创建maven项目没有src/main/java目录问题
- 最全面的SpringMVC教程(六)——WebSocket
热门文章
- Unity笔记之WebGL下载图片、视频
- DayDreamSDK
- select下拉框 笔记
- Qt-FFmpeg开发-视频播放(3)
- windows apache php 500,Windows 环境下的 PHP5 与 Apache 服务_php
- cannot be hot swapped into a running virtal machine的解决方法
- 学习率(Learing Rate)的作用以及如何调整
- Speech and Language Processing (3rd ed. draft) Chapter 2 ——正则表达式,文本归一化,编辑距离 阅读笔记
- Java基本类型变量和引用类型变量
- c# 控件多屏显示全屏功能