本文阅读nacos-2.0.2的config源码,编写示例,分析推送配置、监听配置的原理。

客户端

创建NacosConfigService对象

Properties properties = new Properties();
properties.setProperty(PropertyKeyConst.SERVER_ADDR, NACOS_HOST);
NacosConfigService configService = new NacosConfigService(properties);

构造方法:

public NacosConfigService(Properties properties) throws NacosException {
ValidatorUtils.checkInitParam(properties); initNamespace(properties);
this.configFilterChainManager = new ConfigFilterChainManager(properties);
ServerListManager serverListManager = new ServerListManager(properties);
serverListManager.start(); this.worker = new ClientWorker(this.configFilterChainManager, serverListManager, properties);
// will be deleted in 2.0 later versions
agent = new ServerHttpAgent(serverListManager);
}
  1. 创建ConfigFilterChainManager - 过滤器链
  2. 创建ServerListManager - 服务器列表管理
  3. 创建ClientWorker - 用来发送请求,内部封装了一个ConfigRpcTransportClient类型对象agent,它能够获取到RpcClient与服务端进行通信

推送配置

Properties properties = new Properties();
properties.setProperty(PropertyKeyConst.SERVER_ADDR, NACOS_HOST); NacosConfigService configService = new NacosConfigService(properties); StringWriter out = new StringWriter();
properties.store(out, "test config"); // 推送配置到nacos服务器
configService.publishConfig(
ORDER_SERVICE, Constants.DEFAULT_GROUP, out.toString(), "properties");

推送配置到nacos服务器:

public boolean publishConfig(String dataId,
String group,
String content,
String type) throws NacosException {
return publishConfigInner(namespace, dataId, group, null, null, null, content, type, null);
} private boolean publishConfigInner(String tenant, String dataId, String group, String tag, String appName,
String betaIps, String content, String type, String casMd5) throws NacosException {
group = blank2defaultGroup(group);
ParamUtils.checkParam(dataId, group, content); ConfigRequest cr = new ConfigRequest();
cr.setDataId(dataId);
cr.setTenant(tenant);
cr.setGroup(group);
cr.setContent(content);
cr.setType(type);
configFilterChainManager.doFilter(cr, null);
content = cr.getContent();
String encryptedDataKey = (String) cr.getParameter("encryptedDataKey"); return worker.publishConfig(
dataId, group, tenant, appName, tag, betaIps, content, encryptedDataKey, casMd5, type);
}

worker使用agent推送配置:

// 1. 封装ConfigPublishRequest对象
ConfigPublishRequest request = new ConfigPublishRequest(dataId, group, tenant, content);
request.setCasMd5(casMd5);
request.putAdditionalParam(TAG_PARAM, tag);
request.putAdditionalParam(APP_NAME_PARAM, appName);
request.putAdditionalParam(BETAIPS_PARAM, betaIps);
request.putAdditionalParam(TYPE_PARAM, type);
request.putAdditionalParam(ENCRYPTED_DATA_KEY_PARAM, encryptedDataKey);
// 2. 获取RpcClient对象
// 3. 使用RpcClient发请求
ConfigPublishResponse response = (ConfigPublishResponse) requestProxy(getOneRunningClient(), request);

监听配置

Properties properties = new Properties();
properties.setProperty(PropertyKeyConst.SERVER_ADDR, NACOS_HOST); NacosConfigService configService = new NacosConfigService(properties); // 添加监听器
configService.addListener(ORDER_SERVICE, Constants.DEFAULT_GROUP, new AbstractListener() {
@Override
public void receiveConfigInfo(String configInfo) {
System.out.printf(">> config: \n%s\n\n", configInfo);
}
});
  1. 将监听器注册到本地,本地使用CacheData作为监听器管理器,封装配置名、dataId和监听器集合,内部使用CopyOnWriteArrayList保存监听器,如果是第一次监听,会先拉取一次配置。本地注册表为Map结构,使用dataId+group+tenant作为key,value是CacheData对象
  2. 在client初始化阶段,会注册一个ServerRequestHandler,专门处理服务器端的ConfigChangeNotifyRequest请求,该请求只会推送变化了的配置的基本信息,而不包括内容,所以此处还会触发一次ConfigBatchListenRequest请求
  3. 之后查找到变化的配置后,再发一个查询请求拉取配置

服务端

推送配置处理器

ConfigPublishRequestHandler处理器

配置中心使用ConfigPublishRequestHandler类处理客户端配置推送请求:

  1. 验证请求参数

  2. 封装configAdvanceInfo配置扩展信息

  3. 创建ConfigInfo封装配置信息

  4. 使用持久层对象insert或者update配置

  5. 使用ConfigChangePublisher推送一个ConfigDataChangeEvent事件

    ConfigChangePublisher.notifyConfigChange(
    new ConfigDataChangeEvent(false, dataId, group, tenant, time.getTime()));

ConfigDataChangeEvent事件处理

ConfigDataChangeEvent事件会触发数据dump操作和集群同步操作。

此处先介绍数据dump操作:

dumpService.dump(syncRequest.getDataId(), syncRequest.getGroup(), syncRequest.getTenant(),
syncRequest.getTag(), syncRequest.getLastModified(), NetUtils.localIP());

dumpService是Dump data service用于备份数据:

// Add DumpTask to TaskManager, it will execute asynchronously.
public void dump(String dataId, String group, String tenant, String tag, long lastModified, String handleIp,
boolean isBeta) {
String groupKey = GroupKey2.getKey(dataId, group, tenant);
String taskKey = String.join("+", dataId, group, tenant, String.valueOf(isBeta), tag);
// 推送一个DumpTask
dumpTaskMgr.addTask(taskKey, new DumpTask(groupKey, tag, lastModified, handleIp, isBeta));
}

DumpTask将使用DumpProcessor类处理:

  1. 把数据写到磁盘
  2. 推送LocalDataChangeEvent事件

LocalDataChangeEvent事件会被以下几个类处理:

  • LongPollingService$1会触发DataChangeTask,响应长轮询订阅者
  • RpcConfigChangeNotifier将数据变化推送给rpc订阅者
  • InternalConfigChangeNotifier处理内部配置文件变化

监听配置处理器

ConfigChangeBatchListenRequestHandler处理器

处理客户端的配置监听请求:

public class ConfigChangeBatchListenRequestHandler
extends RequestHandler<ConfigBatchListenRequest, ConfigChangeBatchListenResponse> { @Autowired
private ConfigChangeListenContext configChangeListenContext; @TpsControl(pointName = "ConfigListen")
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
public ConfigChangeBatchListenResponse handle(
ConfigBatchListenRequest configChangeListenRequest, RequestMeta meta)
throws NacosException {
String connectionId = StringPool.get(meta.getConnectionId());
String tag = configChangeListenRequest.getHeader(Constants.VIPSERVER_TAG); ConfigChangeBatchListenResponse configChangeBatchListenResponse =
new ConfigChangeBatchListenResponse();
for (ConfigBatchListenRequest.ConfigListenContext listenContext : configChangeListenRequest
.getConfigListenContexts()) {
String groupKey = GroupKey2
.getKey(listenContext.getDataId(), listenContext.getGroup(), listenContext.getTenant());
groupKey = StringPool.get(groupKey); String md5 = StringPool.get(listenContext.getMd5()); // 监听
if (configChangeListenRequest.isListen()) {
// 注册监听器,维护groupKey->connectionId集关系和groupKey->md5关系
configChangeListenContext.addListen(groupKey, md5, connectionId);
// 判断变化
boolean isUptoDate = ConfigCacheService.isUptodate(groupKey, md5, meta.getClientIp(), tag);
if (!isUptoDate) {
// 把变化的配置基本信息添加到响应
configChangeBatchListenResponse
.addChangeConfig(listenContext.getDataId(), listenContext.getGroup(),
listenContext.getTenant());
}
} else {
// 取消监听
configChangeListenContext.removeListen(groupKey, connectionId);
}
} return configChangeBatchListenResponse;
}
}

Nacos源码 (4) 配置中心的更多相关文章

  1. Nacos源码结构和AP模式注册中心实现介绍

    前言 NacosAP模式源码分析目录 微服务下的注册中心如何选择 Nacos使用和注册部分源码介绍 Nacos服务心跳和健康检查源码介绍 Nacos服务发现 Nacos源码结构介绍 Nacos版本基于 ...

  2. Nacos源码系列—关于服务注册的那些事

    点赞再看,养成习惯,微信搜索[牧小农]关注我获取更多资讯,风里雨里,小农等你,很高兴能够成为你的朋友. 项目源码地址:公众号回复 nacos,即可免费获取源码 简介 首先我们在看Nacos源码之前,要 ...

  3. Dubbo源码学习--注册中心分析

    相关文章: Dubbo源码学习--服务是如何发布的 Dubbo源码学习--服务是如何引用的 注册中心 关于注册中心,Dubbo提供了多个实现方式,有比较成熟的使用zookeeper 和 redis 的 ...

  4. EDKII Build Process:EDKII项目源码的配置、编译流程[三]

    <EDKII Build Process:EDKII项目源码的配置.编译流程[3]>博文目录: 3. EDKII Build Process(EDKII项目源码的配置.编译流程) -> ...

  5. httpd的rpm包及源码安装配置

    httpd的rpm包及源码安装配置 1.rpm包安装 系统环境: [root@zhaochj ~]# cat /etc/issue CentOS release 6.4 (Final) Kernel ...

  6. 源码编译配置lnmp部署zabbix

    环境说明: [root@wcy ~]# cat /etc/redhat-release CentOS release 6.9 (Final) [root@wcy ~]# uname -a Linux ...

  7. renren-fast后端源码参考-配置和对应工具

    1. renren-fast后端源码参考-配置和对应工具 1.1. 前言 renren-fast是个开源的前后端分离快速开放平台,没有自己框架的同学可以直接使用它的,而我打算浏览一遍它的代码,提取一些 ...

  8. Eureka详解系列(四)--Eureka Client部分的源码和配置

    简介 按照原定的计划,我将分三个部分来分析 Eureka 的源码: Eureka 的配置体系(已经写完,见Eureka详解系列(三)--探索Eureka强大的配置体系): Eureka Client ...

  9. Eureka详解系列(五)--Eureka Server部分的源码和配置

    简介 按照原定的计划,我将分三个部分来分析 Eureka 的源码: Eureka 的配置体系(已经写完,见Eureka详解系列(三)--探索Eureka强大的配置体系): Eureka Client ...

  10. Spring Cloud入门-Nacos实现注册和配置中心(Hoxton版本)

    文章目录 摘要 Nacos简介 使用Nacos作为注册中心 安装并运行Nacos 创建应用注册到Nacos 负载均衡功能 使用Nacos作为配置中心 创建nacos-config-client模块 在 ...

随机推荐

  1. LeetCode456:132模式(单调栈)

    解题思路:根据题意,我们首先首先要找到所有的极大值点,同时记录当前极大值点的左边的最小值.遍历所有点,看是否能够满足132条件.虽然记录极大值点的地方可以优化,减小比较的次数,但是由于我们不知道极大值 ...

  2. Tarjan 学习笔记

    萌新刚学Tarjan,啥也不会,肯定一堆错,请大佬指正谢谢 前置 强连通 强连通: 在不是强连通图的有向图\(G\)内,其顶点\(u\),\(v\)两个方向上都存在有向路径,则\(u\)和\(v\)强 ...

  3. 同样的SQL,怎么突然就慢了?

    本篇文章素材来源于某银行系统的一次性能问题分析. 许久没写这种troubleshooting类型的技术文章了,因为曾在服务公司呆过多年,工作原因,这方面之前做的多,听的更多,导致已经达到在自己认知维度 ...

  4. MagicArray:像php一样,让Go业务代码不再卷!

    卷!一个字形容了当今码农行业的现状.一层一层的代码结构,一个一个dto,entity,vo,req,resp.这些对象越来越多,让码农们非常劳于奔命,加一个字段,改一个字段,影响面有多少.代码量也越来 ...

  5. 37. 干货系列从零用Rust编写负载均衡及代理,负载均衡中try_files实现

    wmproxy wmproxy已用Rust实现http/https代理, socks5代理, 反向代理, 静态文件服务器,四层TCP/UDP转发,七层负载均衡,内网穿透,后续将实现websocket代 ...

  6. 神经网络优化篇:详解梯度消失/梯度爆炸(Vanishing / Exploding gradients)

    梯度消失/梯度爆炸 训练神经网络,尤其是深度神经所面临的一个问题就是梯度消失或梯度爆炸,也就是训练神经网络的时候,导数或坡度有时会变得非常大,或者非常小,甚至于以指数方式变小,这加大了训练的难度. 接 ...

  7. Win11环境Mecab日语分词和词性分析以及动态库DLL not found问题(Python3.10)

    日语因为存在假名,会导致翻译软件进行翻译时机翻味道过重的问题,比如積ん読(つんどく)这个词,大多数软件会翻译成:堆积的读,但其实是明明买了书却不读,光放着的意思.有时候也需要单独查句子中的单词释义来理 ...

  8. MySQL面试题:一条SQL语句在MySQL中执行过程全解析

    一 .MySQL 基础架构分析 介绍一下下图涉及的一些组件的基本作用帮助大家理解这幅图. 连接/线程处理(连接器): 身份认证和权限相关(如连接处理.授权认证.安全等等). 查询缓存: 执行查询语句的 ...

  9. ORA-28579 在从外部过程代理程序回调时,发生网络错误,ORA-06512 在"SDE.ST_GEOMETRY_SHAPELIB_PKG"

    如图所示,在执行sde.st_transform方法时报错. 环境是arcgis10.8  oracle是12C,版本号是v12.1.0.2.0 但是执行ST_GEOMETRY方法是可以的,说明配置没 ...

  10. 聊聊ChatGLM中P-tuning v2的应用

    论文PDF地址:https://arxiv.org/pdf/2110.07602.pdf 转载请备注出处:https://www.cnblogs.com/zhiyong-ITNote/ P-Tunin ...