分析Nacos配置及源码

Nacos 配置中心源码分析 配置加载有三个方法 loadSharedConfiguration、loadSharedConfiguration、 loadApplicationConfiguration 以 loadApplicationConfiguration 继续跟进。private void loadApplicationConfiguration(CompositePropertySource compositePropertySource, String dataIdPrefix,NacosConfigProperties 阅读详情

Nacos作为配置中心,必然需要保证服务节点的高可用性,大家思考一个问题,Nacos是如何实现集群的呢?带着这个疑问我们往下看,下面的图片,表示了Nacos集群的部署图。

Nacos集群工作原理

Nacos作为配置中心的集群结构中,是一种无中心化节点的设计,由于没有主从节点,也没有选举机制,所以为了能够实现热备,就需要增加虚拟IP(VIP)。

Nacos的数据存储分为两部分

/data/program/nacos-1/data/config-data/${GROUP}

在Nacos的设计中,Mysql是一个中心数据仓库,且认为在Mysql中的数据是绝对正确的。 除此之外,Nacos在启动时会把Mysql中的数据写一份到本地磁盘。

这么设计的好处是可以提高性能,当客户端需要请求某个配置项时,服务端会想Ian从磁盘中读取对应文件返回,而磁盘的读取效率要比数据库效率高。

当配置发生变更时:

  1. Nacos会把变更的配置保存到数据库,然后再写入本地文件。
  2. 接着发送一个HTTP请求,给到集群中的其他节点,其他节点收到事件后,从Mysql中dump刚刚写入的数据到本地文件中。

另外,NacosServer启动后,会同步启动一个定时任务,每隔6小时,会dump一次全量数据到本地文件

配置变更同步入口

当配置发生修改、删除、新增操作时,通过发布一个 notifyConfigChange 事件。

@PostMapping@Secured(action = ActionTypes.WRITE, parser = ConfigResourceParser.class)public Boolean publishConfig(HttpServletRequest request, HttpServletResponse response,        @RequestParam(value = "dataId") String dataId, @RequestParam(value = "group") String group,        @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant,        @RequestParam(value = "content") String content, @RequestParam(value = "tag", required = false) String tag,        @RequestParam(value = "appName", required = false) String appName,        @RequestParam(value = "src_user", required = false) String srcUser,        @RequestParam(value = "config_tags", required = false) String configTags,        @RequestParam(value = "desc", required = false) String desc,        @RequestParam(value = "use", required = false) String use,        @RequestParam(value = "effect", required = false) String effect,        @RequestParam(value = "type", required = false) String type,        @RequestParam(value = "schema", required = false) String schema) throws NacosException {       //省略..    if (StringUtils.isBlank(betaIps)) {        if (StringUtils.isBlank(tag)) {            persistService.insertOrUpdate(srcIp, srcUser, configInfo, time, configAdvanceInfo, true);            ConfigChangePublisher                    .notifyConfigChange(new ConfigDataChangeEvent(false, dataId, group, tenant, time.getTime()));        } else {            persistService.insertOrUpdateTag(configInfo, tag, srcIp, srcUser, time, true);            ConfigChangePublisher.notifyConfigChange(                    new ConfigDataChangeEvent(false, dataId, group, tenant, tag, time.getTime()));        }    }//省略    return true;}

AsyncNotifyService

配置数据变更事件,专门有一个监听器AsyncNotifyService,它会处理数据变更后的同步事件。

@Autowiredpublic AsyncNotifyService(ServerMemberManager memberManager) {    this.memberManager = memberManager;        // Register ConfigDataChangeEvent to NotifyCenter.    NotifyCenter.registerToPublisher(ConfigDataChangeEvent.class, NotifyCenter.ringBufferSize);        // Register A Subscriber to subscribe ConfigDataChangeEvent.    NotifyCenter.registerSubscriber(new Subscriber() {                @Override        public void onEvent(Event event) {            // Generate ConfigDataChangeEvent concurrently            if (event instanceof ConfigDataChangeEvent) {                ConfigDataChangeEvent evt = (ConfigDataChangeEvent) event;                long dumpTs = evt.lastModifiedTs;                String dataId = evt.dataId;                String group = evt.group;                String tenant = evt.tenant;                String tag = evt.tag;                Collection<Member> ipList = memberManager.allMembers(); //得到集群中的ip列表                                // 构建NotifySingleTask,并添加到队列中。                Queue<NotifySingleTask> queue = new LinkedList<NotifySingleTask>();                for (Member member : ipList) { //遍历集群中的每个节点                    queue.add(new NotifySingleTask(dataId, group, tenant, tag, dumpTs, member.getAddress(),                            evt.isBeta));                }                //异步执行任务 AsyncTask                ConfigExecutor.executeAsyncNotify(new AsyncTask(nacosAsyncRestTemplate, queue));            }        }                @Override        public Class<? extends Event> subscribeType() {            return ConfigDataChangeEvent.class;        }    });}

AsyncTask

@Overridepublic void run() {    executeAsyncInvoke();}private void executeAsyncInvoke() {    while (!queue.isEmpty()) {//遍历队列中的数据,直到数据为空        NotifySingleTask task = queue.poll(); //获取task        String targetIp = task.getTargetIP(); //获取目标ip                if (memberManager.hasMember(targetIp)) { //如果集群中的ip列表包含目标ip            // start the health check and there are ips that are not monitored, put them directly in the notification queue, otherwise notify            //判断目标ip的健康状态            boolean unHealthNeedDelay = memberManager.isUnHealth(targetIp); //            if (unHealthNeedDelay) { //如果目标服务是非健康,则继续添加到队列中,延后再执行。                // target ip is unhealthy, then put it in the notification list                ConfigTraceService.logNotifyEvent(task.getDataId(), task.getGroup(), task.getTenant(), null,                        task.getLastModified(), InetUtils.getSelfIP(), ConfigTraceService.NOTIFY_EVENT_UNHEALTH,                        0, task.target);                // get delay time and set fail count to the task                asyncTaskExecute(task);            } else {                //构建header                Header header = Header.newInstance();                header.addParam(NotifyService.NOTIFY_HEADER_LAST_MODIFIED, String.valueOf(task.getLastModified()));                header.addParam(NotifyService.NOTIFY_HEADER_OP_HANDLE_IP, InetUtils.getSelfIP());                if (task.isBeta) {                    header.addParam("isBeta", "true");                }                AuthHeaderUtil.addIdentityToHeader(header);                //通过restTemplate发起远程调用,如果调用成功,则执行AsyncNotifyCallBack的回调方法                restTemplate.get(task.url, header, Query.EMPTY, String.class, new AsyncNotifyCallBack(task));            }        }    }}
@GetMapping("/dataChange")public Boolean notifyConfigInfo(HttpServletRequest request, @RequestParam("dataId") String dataId,        @RequestParam("group") String group,        @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant,        @RequestParam(value = "tag", required = false) String tag) {    dataId = dataId.trim();    group = group.trim();    String lastModified = request.getHeader(NotifyService.NOTIFY_HEADER_LAST_MODIFIED);    long lastModifiedTs = StringUtils.isEmpty(lastModified) ? -1 : Long.parseLong(lastModified);    String handleIp = request.getHeader(NotifyService.NOTIFY_HEADER_OP_HANDLE_IP);    String isBetaStr = request.getHeader("isBeta");    if (StringUtils.isNotBlank(isBetaStr) && trueStr.equals(isBetaStr)) {        dumpService.dump(dataId, group, tenant, lastModifiedTs, handleIp, true);    } else {        //        dumpService.dump(dataId, group, tenant, tag, lastModifiedTs, handleIp);    }    return true;}

dumpService.dump用来实现配置的更新,代码如下

当前任务会被添加到DumpTaskMgr中管理。

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);    dumpTaskMgr.addTask(taskKey, new DumpTask(groupKey, tag, lastModified, handleIp, isBeta));    DUMP_LOG.info("[dump-task] add task. groupKey={}, taskKey={}", groupKey, taskKey);}

TaskManager.addTask, 先调用父类去完成任务添加。

@Overridepublic void addTask(Object key, AbstractDelayTask newTask) {    super.addTask(key, newTask);    MetricsMonitor.getDumpTaskMonitor().set(tasks.size());}

在这种场景设计中,一般都会采用生产者消费者模式来完成,因此这里不难猜测到,任务会被保存到一个队列中,然后有另外一个线程来执行。

NacosDelayTaskExecuteEngine

TaskManager的父类是
NacosDelayTaskExecuteEngine,

这个类中有一个成员属性 protected final ConcurrentHashMap<Object, AbstractDelayTask> tasks; ,专门来保存延期执行的任务类型AbstractDelayTask.

在这个类的构造方法中,初始化了一个延期执行的任务,其中具体的任务是ProcessRunnable.

public NacosDelayTaskExecuteEngine(String name, int initCapacity, Logger logger, long processInterval) {    super(logger);    tasks = new ConcurrentHashMap<Object, AbstractDelayTask>(initCapacity);    processingExecutor = ExecutorFactory.newSingleScheduledExecutorService(new NameThreadFactory(name));    processingExecutor            .scheduleWithFixedDelay(new ProcessRunnable(), processInterval, processInterval, TimeUnit.MILLISECONDS);}

ProcessRunnable

private class ProcessRunnable implements Runnable {        @Override    public void run() {        try {            processTasks();        } catch (Throwable e) {            getEngineLog().error(e.toString(), e);        }    }}

processTasks

protected void processTasks() {    //获取所有的任务    Collection<Object> keys = getAllTaskKeys();    for (Object taskKey : keys) {        AbstractDelayTask task = removeTask(taskKey);        if (null == task) {            continue;        }        //获取任务处理器,这里返回的是DumpProcessor        NacosTaskProcessor processor = getProcessor(taskKey);        if (null == processor) {            getEngineLog().error("processor not found for task, so discarded. " + task);            continue;        }        try {            // ReAdd task if process failed            //执行具体任务            if (!processor.process(task)) {                retryFailedTask(taskKey, task);            }        } catch (Throwable e) {            getEngineLog().error("Nacos task execute error : " + e.toString(), e);            retryFailedTask(taskKey, task);        }    }}

DumpProcessor.process

读取数据库的最新数据,然后更新本地缓存和磁盘。

 

Nacos 配置中心 Server 端源码分析 前面我们分析了 Nacos 配置中心 Client 端的源码和 Nacos 配置中心热更新的源码,二者最终都会去 Nacos Server 获取最新配置,本篇我们来分析 Nacos Server 端配置中心相关源码。 阅读详情

相关推荐

鸿蒙之HML语法参考

HML(HarmonyOS Markup Language)是一套类HTML的标记语言,通过组件,事件构建出页面的内容。页面具备数据绑定、事件绑定、条件渲染和逻辑控制等高级能力。

2401_82772199的博客 1649

Nacos 源码环境搭建

最近在学习nacos,通过调式源码查看服务注册和发现流程和原理,本地部署naos源码需要一定的步骤,本文主要做nacos源码部署。

qq_42700121的博客 966

ZYNQ软件复位重启、程序跳转的实现方法(Multiboot)

目录一、增加FSBL的调试信息 一、增加FSBL的调试信息 启动过程的调试信息如下: Xilinx First Stage Boot Loader Release 2016.4 Jan 19 2020-10:35:00 Devcfg driver initialized Silicon Version 3.1 Boot mode is QSPI Single Flash Informat...

weixin_41922484的博客 2万+

nacos部署

1、jdk1.8 2、下载源码包:nacos-server-2.0.3.tar.gz Releases · alibaba/nacos · GitHub

weixin_44012561的博客 514

基于Android Studio的校园智能点餐系统开发实战(附完整源码与部署指南)

本文详细介绍了基于Android Studio开发校园智能点餐系统的完整流程,包括需求分析、环境搭建、数据库设计、客户端与后台功能实现。系统采用SSM框架和MySQL数据库,解决了校园食堂高峰期排队问题,提供高效的点餐体验。附完整源码与部署指南,助力开发者快速构建类似应用。

weixin_29015801的博客 560

PCB盲埋孔工艺全解析:从机械钻孔到激光钻孔的进阶之路

本文全面解析了PCB盲埋孔工艺,对比了机械钻孔与激光钻孔两种核心技术的工艺流程、优势与局限。机械钻孔成本低、工艺成熟,适用于常规孔径;激光钻孔则能实现微孔、叠孔等复杂HDI结构,精度更高。文章深入探讨了如何根据孔径、成本、板厚及产品需求选择合适工艺,并强调了设计与制造协同的重要性,为高密度PCB设计提供实用指南。

weixin_29306317的博客 686

Spring Cloud Alibaba系列(5)之:nacos源码本地环境搭建

nacos自带了一套简单的认证权限管理体系,在多数情况下已经够用了。

weigeshikebi的博客 1530

gurobi最新下载安装教程 2023.11

我下载的最新版gurobi支持python3.7-python3.11,这个序号与计算机硬件捆绑,一旦激活就会失效。运行激活码之后,产生的。如果既放置在了上面的默认搜索目录,又设置了。系统环境变量(不是用户环境变量),指向保存的目录和文件名(例如。高版本产生的许可文件兼容低版本。验证不会成功,学术许可不会激活。之后cmd进入命令行,cd进入gurobi所在的。,实测不到一小时即可审核完毕,获得对应的许可。如果学校的网关没有将本校的。系统环境变量指向不同的目录和文件,原厂无关,是学校网络的设置问题。

Cachel Wood的博客 2万+

Nacos2.0.3源码集群部署

nacos源码集群部署

lightofsms的博客 821

当启动容器的时候提示:exec format error?如何解决问题

Docker容器出现"exec format error"的主要原因是架构不兼容(如ARM设备运行x86镜像)。解决方案包括:1)匹配镜像与宿主机架构;2)构建时指定平台;3)安装QEMU模拟器;4)修复文件权限和格式;5)验证基础镜像兼容性。排查流程应从检查架构匹配开始,逐步验证权限、解释器和换行符等问题。建议优先使用多架构镜像,构建时明确指定平台,并使用buildx工具生成多平台镜像。调试时可进入容器手动验证文件格式和兼容性。

weixin_39291964的博客 2721

Qt | QPalette 类(调色版)

Qt | QPalette 类(调色版)

Qt历险记,专注Qt技术,三年医疗设备研发经验,1年机器视觉研发经验,半年柔性传感研发经验。 776

nacos2.2.3基础部署

配置自定义身份识别的key(不可为空)和value(不可为空) nacos.core.auth.server.identity.key=example。2:解压安装包 tar -xvf nacos-server-2.2.3.tar.gz。3:修改配置文件(由于2.2.3版本很多默认值默认都为空,所以需要我们手动添加一下)1:上传安装包到linux系统。nacos2.2.3基础部署。3.2开启鉴权功能设置密钥。3.1:修改数据库配置。

X_yufei的博客 5011

基于ESP32的智能无线广播系统开发博客

通过本项目的开发,我们成功地实现了基于ESP32的智能无线广播系统。该系统不仅具备传统广播的功能,还实现了通过Wi-Fi无线传输音频、实时广播和智能控制等多种先进功能。未来,随着技术的进一步发展,我们还可以扩展更多功能,如智能推荐广播内容、支持语音识别和控制等。希望本项目能够为嵌入式开发人员提供一些思路和参考,帮助大家在实际开发中更好地应用ESP32平台,开发出更加智能和实用的无线广播系统。

2201_76125393的博客 424

Nacos配置中心源码分析

1. 什么是Nacos ? Nacos主要用做注册中心和配置中心。Nacos介绍,Nacos用法, Nacos源码下载 etc… 请查看Nacos官方文档, 本文基于nacos版本1.2.0进行分析。 2. Nacos代码入口 从官方文档给的JAVA SDK 入手, 这样可以知道使用流程,也可以通过入口,分析代码。官方给的代码如下: try { String serverAddr = "...

编号94530 1253

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Nacos源码—Nacos配置中心实现分析

Nacos是按如下方式整合SpringBoot去读取远程配置数据的:在SpringBoot项目启动的过程中,有一个步骤是准备上下文,该步骤中就会去加载配置文件,加载配置文件时就会调用Nacos提供的获取配置数据的HTTP接口,最终完成从Nacos服务端拉获取置数据的整个流程,并且在获取到配置数据后会将数据持久化到本地。SpringBoot在启动过程中,会调用Nacos实现的加载配置文件扩展接口PropertySourceLocator,从而实现加载Nacos配置中心的远程配置文件。

kfashfasf的博客 878

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Nacos源码—5.Nacos配置中心实现分析一

但要注意共享配置文件里的配置不要和自身应用配置文件里的配置重复,因为自身应用配置文件比共享配置文件的优先级高。在调用PropertySourceLocator实现类的locateCollection()方法时,会先调用PropertySourceLocator扩展接口的locateCollection()方法,从而才会触发调用PropertySourceLocator实现类实现的locate()方法,比如调用NacosPropertySourceLocator的locate()方法。

mjunz的博客 1247

Nacos源码—5.Nacos配置中心实现分析

在异步任务AsyncTask的run()方法中,会一直从queue中获取通知任务,以便将配置数据同步到对应的集群节点。在调用PropertySourceLocator实现类的locateCollection()方法时,会先调用PropertySourceLocator扩展接口的locateCollection()方法,从而才会触发调用PropertySourceLocator实现类实现的locate()方法,比如调用NacosPropertySourceLocator的locate()方法。

weixin_36393674的博客 113

springcloud/springboot集成NACOS 做注册和配置中心以及nacos源码分析

一、SpringCloud 简介Spring Cloud 是一系列框架的有序集合如服务发现注册、配置中心、消息总线、负载均衡、熔断器、数据监控等。SpringCloud 将多个服务框架组合起来,通过Spring Boot进行再封装,屏蔽掉了复杂的配置和实现原理,最终给开发者提供了一套简单易懂、易部署和易维护的分布式系统开发...

cnzzs的博客 1651

nacos配置中心源码分析——拉取配置信息

个人认为分析配置中心的源码要比注册中心更需要spring boot源码的理解,主要体现在更新或者刷新容器内部配置文件内容。主要分为两部分讲解:1、spring boot启动集成nacos及拉取远端配置信息;2、nacos动态更新配置,客户端如何感知并在未重启应用的情况下如何刷新配置。

weixin_36279234的博客 4799

Nacos2.4.0源码分析05 nacos的配置发布

在发布配置时,会将配置信息存入数据库,然后发布一个ConfigDataChangeEvent事件,而处理这个事件的入口有两个分别是DumpService、AsyncNotifyService。AsyncNotifyService所有的服务端实例都会调用各自的DumpService 更新本地磁盘中的配置数据。

qq_38201262的博客 738

nacos配置中心源码分析

这个里面先查出mysql最大的主键量,然后分页每次1000条写入磁盘和内存ConfigCacheService.dump(cf.getDataId(),cf.getGroup(),cf.getTenant(),cf.getContent(),cf.getLastModified(),cf.getType());触发本地文件和内存的更新。其中RefreshScopeRefreshedEvent是一个留给我们的扩展点,我们可以自己去监听这个变更的事件,这里没有实现这个类的监听。............

xjk201的博客 1649
上一篇: 让Redis循环写入时提高10倍的技巧
下一篇: 排查Spring 无法启动的问题
Java_ttcd
博客等级 码龄4年 198粉丝 190原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值