【Nacos源码之配置管理 四】DumpService如何将配置文件全部Dump到磁盘中

简介: 本文分析一下,Nacos在启动的时候是怎么将所有的配置文件信息 Dump到磁盘上的; 读完本文,你将了解到以下知识• [x] 项目启动之初Dump配置数据的流程• [x] 如何快速启动(isQuickStart:快速启动不用全量Dump配置信息)• [x] DiskUtil.heartBeatFile 这个心跳文件的作用• [x] 全量Dump执行器DumpAllProcessor• [x] AggrWhitelist这个类是作用是什么• [x] ClientIpWhiteList作用是什么• [x] SwitchService作用是什么• [x] Dump配置数据的事件通知

本文分析一下,Nacos在启动的时候是怎么将所有的配置文件信息 Dump到磁盘上的; 读完本文,你将了解到以下知识

  • [x] 项目启动之初Dump配置数据的流程
  • [x] 如何快速启动(isQuickStart:快速启动不用全量Dump配置信息)
  • [x] DiskUtil.heartBeatFile 这个心跳文件的作用
  • [x] 全量Dump执行器DumpAllProcessor
  • [x] AggrWhitelist这个类是作用是什么
  • [x] ClientIpWhiteList作用是什么
  • [x] SwitchService作用是什么
  • [x] Dump配置数据的事件通知

Dump文件的类在 DumpService 中,所以我们主要分析这个类

Part1DumpService初始化

Spring启动加载时,会执行带有@PostConstruct 注解的初始化方法;

 

@PostConstruct
    public void init() {
        DumpAllProcessor dumpAllProcessor = new DumpAllProcessor(this);
        dumpTaskMgr = new TaskManager("com.alibaba.nacos.server.DumpTaskManager",new DumpProcessor(this));
        dumpAllTaskMgr = new TaskManager("com.alibaba.nacos.server.DumpAllTaskManager",dumpAllProcessor);
        try {
            //全量Dump配置信息
            dumpConfigInfo(dumpAllProcessor);
            DiskUtil.clearAllBeta();
            if (persistService.isExistTable(BETA_TABLE_NAME)) {
                new DumpAllBetaProcessor(this).process(DumpAllBetaTask.TASK_ID, new DumpAllBetaTask());
            }
            // 更新Tag缓存
            DiskUtil.clearAllTag();
            if (persistService.isExistTable(TAG_TABLE_NAME)) {
                new DumpAllTagProcessor(this).process(DumpAllTagTask.TASK_ID, new DumpAllTagTask());
            }
            // add to dump aggr
            List<ConfigInfoChanged> configList = persistService.findAllAggrGroup();
            if (!CollectionUtils.isEmpty(configList)) {
                total = configList.size();
                List<List<ConfigInfoChanged>> splitList = splitList(configList, INIT_THREAD_COUNT);
                for (List<ConfigInfoChanged> list : splitList) {
                    MergeAllDataWorker work = new MergeAllDataWorker(list);
                    work.start();
                }
                log.info("server start, schedule merge end.");
            }
        } catch (Exception e) {
        }
        if (!STANDALONE_MODE) {
            //Write the current time to the status/heartBeat.txt file every 10 seconds
            TimerTaskService.scheduleWithFixedDelay(()-> heartbeat(), 0, 10, TimeUnit.SECONDS);
            long initialDelay = new Random().nextInt(INITIAL_DELAY_IN_MINUTE) + 10;
            //Full Dump every DUMP_ALL_INTERVAL_IN_MINUTE minutes
            TimerTaskService.scheduleWithFixedDelay(()-> dumpAllTaskMgr.addTask(DumpAllTask.TASK_ID, new DumpAllTask()) ,
                initialDelay, DUMP_ALL_INTERVAL_IN_MINUTE,
                TimeUnit.MINUTES);
            //Full Dump every DUMP_ALL_INTERVAL_IN_MINUTE minutes 
            TimerTaskService.scheduleWithFixedDelay(()-> dumpAllTaskMgr.addTask(DumpAllBetaTask.TASK_ID,
                new DumpAllBetaTask()), initialDelay, DUMP_ALL_INTERVAL_IN_MINUTE,
                TimeUnit.MINUTES);
        }
        //Try to Delete the expiration history configuration record every ten minutes
        TimerTaskService.scheduleWithFixedDelay(()-> clearConfigHistory(),
            10, 10, TimeUnit.MINUTES);
    }

上面的代码就是DumpService初始化之后调用的初始化方法;我们现在来深度解析一下究竟做了哪些事情呢?

1全量Dump配置信息

类中的方法 dumpConfigInfo(dumpAllProcessor); 里面主要是将数据库中的所有ConfigInfo查询出来写到服务器的磁盘中; 方法中传入了一个 dumpAllProcessor 对象; 这个是一个TaskProcessor 任务处理器; 在上一篇文章中我们介绍了【Nacos源码 三】TaskManager 任务管理的使用; 所有看这里就很容易理解了; dumpAllProcessor中有个process()方法; 最终执行任务的时候就是执行这个方法的; 那我们进入dumpConfigInfo看看他做了什么?

dumpConfigInfo方法保存配置数据到磁盘

private void dumpConfigInfo(DumpAllProcessor dumpAllProcessor)
        throws IOException {
        int timeStep = 6;
        Boolean isAllDump = true;
        // initial dump all
        FileInputStream fis = null;
        Timestamp heartheatLastStamp = null;
        try {
            if (isQuickStart()) {
                File heartbeatFile = DiskUtil.heartBeatFile();
                if (heartbeatFile.exists()) {
                    fis = new FileInputStream(heartbeatFile);
                    String heartheatTempLast = IOUtils.toString(fis,
                        Constants.ENCODE);
                    heartheatLastStamp = Timestamp.valueOf(heartheatTempLast);
                    if (TimeUtils.getCurrentTime().getTime()
                        - heartheatLastStamp.getTime() < timeStep * 60 * 60 * 1000) {
                        isAllDump = false;
                    }
                }
            }
            if (isAllDump) {
                DiskUtil.clearAll();
                dumpAllProcessor.process(DumpAllTask.TASK_ID, new DumpAllTask());
            } else {
                Timestamp beforeTimeStamp = getBeforeStamp(heartheatLastStamp,timeStep);
                DumpChangeProcessor dumpChangeProcessor = new DumpChangeProcessor(
                    this, beforeTimeStamp, TimeUtils.getCurrentTime());
                dumpChangeProcessor.process(DumpChangeTask.TASK_ID,new DumpChangeTask());
                TimerTaskService.scheduleWithFixedDelay(()-> checkMd5AndDumpChange(), 0, 12,
                    TimeUnit.HOURS);
            }
        } catch (IOException e) {
        } finally {
        }
    }

这里的代码 ,是我改动过的,源码里面这个方法写的很乱,不利于阅读,我给重构了一下,并且提了一个PR;不知道Nacos有没有给我合并进去;但是改动之后的左右是没有变化的;

isQuickStart() 判断是否快速启动

val = env.getProperty("isQuickStart");
if (val != null && TRUE_STR.equals(val)) {
    isQuickStart = true;
}

这个方法是判断我们在启动的时候是否指定了  快速启动; 默认为false; 如果想快速启动的话,可以在配置文件 application.properties 加上一行

isQuickStart=true

或者还可以设置Jvm系统属性 ;在启动脚本中加上 参数

JAVA_OPT="${JAVA_OPT} -Dnacos.home=${BASE_DIR}"
##加入isQuickStart=true 
JAVA_OPT="${JAVA_OPT} -DisQuickStart=true"

注意,这个一定不要放在后面;就放在-Dnacos.home 后面就行,不然不会生效; 为什么在Jvm中设置的-D属性能够在Spring中的Environment 中获取到?在另外一篇文章中有讲解到;Spring中配置优先级

DiskUtil.heartBeatFile 获取心跳文件

这个方法是获取心跳文件的方法,心跳文件在 {NACOS_HOME}/status/heartBeat.txt ; 这个{NACOS_HOME} 是获取Jvm属性nacos.home

System.getProperty("nacos.home")

因为在startup.sh 启动脚本中定义的是

export BASE_DIR=`cd $(dirname $0)/..; pwd`
JAVA_OPT="${JAVA_OPT} -Dnacos.home=${BASE_DIR}"

其中dirname $0 表示的是执行当前脚本的路径,-Dnacos.home 就是设置了当前路径; 如果你想改成其他的路径可以改启动脚本;比如改成

JAVA_OPT="${JAVA_OPT} -Dnacos.home=/Users/shirenchuang/mynacos"

如果这里不设置这个Jvm属性nacos.home ;那么{NACOS_HOME} 就会获取系统属性并且加上 后缀File.separator + "nacos"

System.getProperty("user.home")

这个是操作系统属性,比如我的Mac电脑的user.home/Users/shirenchuang ; 那么最终的{NACOS_HOME}=/Users/shirenchuang/nacos

{NACOS_HOME}/status/heartBeat.txt ;是一个心跳文件,每十秒就会把当前时间写入到这个文件中;

heartBeat.txt的作用是什么?为什么要每十秒保存当前时间

因为为了能够快速启动应用,那么我们在启动的时候,可以选择不需要全部Dump所有的配置文件,因为上一次可能已经Dump了文件在磁盘中了,每次启动项目,都DumpAll 配置,如果配置很大的话,走IO还是会花费一定的时间的;所以我们每十秒来持久化一次当前时间,用于记录上一次服务正常距离现在有多长时间;

假设服务宕机了,半个小时之后才启动成功,那么我们只需要将这半小时之内数据库中的配置变化重新Dump到磁盘中就行了,不需要DumpAll; 可以看到代码

if (isQuickStart()) {
   //如果上一次服务正常的时间距离现在不超过6个小时;
   //那么设置 isAllDump = false;表示不需要全量Dump
}

如果上一次服务正常的时间距离现在不超过6个小时;那么设置 isAllDump = false;表示不需要全量Dump;

DumpAllProcessor执行器做全量Dump

这个执行器就是将数据库中的所有ConfigInfo全量Dump到磁盘中去

@Override
    public boolean process(String taskType, AbstractTask task) {
        long currentMaxId = persistService.findConfigMaxId();
        long lastMaxId = 0;
        while (lastMaxId < currentMaxId) {
            Page<PersistService.ConfigInfoWrapper> page = persistService.findAllConfigInfoFragment(lastMaxId,
                PAGE_SIZE);
            if (page != null && page.getPageItems() != null) {
                for (PersistService.ConfigInfoWrapper cf : page.getPageItems()) {
                    long id = cf.getId();
                    lastMaxId = id > lastMaxId ? id : lastMaxId;
                    if (cf.getDataId().equals(AggrWhitelist.AGGRIDS_METADATA)) {
                        AggrWhitelist.load(cf.getContent());
                    }
                    if (cf.getDataId().equals(ClientIpWhiteList.CLIENT_IP_WHITELIST_METADATA)) {
                        ClientIpWhiteList.load(cf.getContent());
                    }
                    if (cf.getDataId().equals(SwitchService.SWITCH_META_DATAID)) {
                        SwitchService.load(cf.getContent());
                    }
                    boolean result = ConfigService.dump(cf.getDataId(), cf.getGroup(), cf.getTenant(), cf.getContent(),
                        cf.getLastModified());
                    final String content = cf.getContent();
                    final String md5 = MD5.getInstance().getMD5String(content);
                    LogUtil.dumpLog.info("[dump-all-ok] {}, {}, length={}, md5={}",
                        GroupKey2.getKey(cf.getDataId(), cf.getGroup()), cf.getLastModified(), content.length(), md5);
                }
                defaultLog.info("[all-dump] {} / {}", lastMaxId, currentMaxId);
            } else {
                lastMaxId += PAGE_SIZE;
            }
        }
        return true;
    }

以上代码很容易看懂,但是有几个特别的地方我们需要详细讲一下;

AggrWhitelist这个类是什么?作用是什么?
static public final String AGGRIDS_METADATA = "com.alibaba.nacos.metadata.aggrIDs";

这个是Nacos立马自定义的一个DataId; 如果ConfigInfo的DataId是这个值的话就会被单独解析,将Content内容解析到

AtomicReference<List<Pattern>> AGGR_DATAID_WHITELIST

Content设置的值可以是一个正则表达式;例如这个AggrWhitelist作用是什么呢?真实意图我也不清楚;但是我只在一个地方看到有调用到这些数据;


/**
    * 增加或更新非聚合数据。
    *
    * @throws NacosException
    */
   @RequestMapping(method = RequestMethod.POST)
   @ResponseBody
   public Boolean publishConfig{
      //省略....
      if (AggrWhitelist.isAggrDataId(dataId)) {
           log.warn("[aggr-conflict] {} attemp to publish single data, {}, {}",
               RequestUtil.getRemoteIp(request), dataId, group);
           throw new NacosException(NacosException.NO_RIGHT, "dataId:" + dataId + " is aggr");
       }
        //省略....
  }

在增加非聚合数据的时候,如果这些DataId刚好匹配到了,就不让添加; 使用场景例如:  我不希望开发者 添加 DataId= com.shirc 开头 的配置;那么我可以怎么做呢? 我们可以在 DataId = com.alibaba.nacos.metadata.aggrIDs 中添加一行数据

com.shirc.*

如下

ClientIpWhiteList是什么?作用是什么

其实看了AggrWhitelist之后,这个ClientIpWhiteList是跟它是差不多意思,也是Nacos自己定义的一个预留配置DataId

 

static public final String CLIENT_IP_WHITELIST_METADATA = "com.alibaba.nacos.metadata.clientIpWhitelist";

值是一个Json

{
 "isOpen": true,
 "ips": ["127.0.0.1", "127.0.0.2"]
}

它的作用,项目中暂时没有使用到,这算是一个预留配置; Ip白名单;

SwitchService是什么?作用是什么?

同上,也是Nacos内部预留的一个配置;DataId是 com.alibaba.nacos.meta.switch ; 开发者可以配置这个里面的属性,来进行一些设置内部属性的操作; 例如已经Nacos中的开关属性有:

## 是否开启固定长轮询
isFixedPolling=false/true
##固定长轮询的间隔时间 只有isFixedPolling=true才生效
fixedPollingInertval=1000
##延迟时间
fixedDelayTime=500

在这里插入图片描述

ConfigService.dump 真正的磁盘写入操作

这个方法首先将配置保存到磁盘文件中,并且缓存配置信息的MD5到内存中;如果配置信息不一致(MD5不一致),则将会发送一个通知事件 LocalDataChangeEvent 告知本地数据有更改; 这里的源码就不贴了,我概述一下操作流程CacheItem 是配置信息的对象;保存着配置信息的一些信息,但是没有保存Content,只保存了content的MD5;

  1. 如果内存中没有当前配置的缓存 CacheItem,则组装对象保存进去;这个时候的md5是空字符串;
  2. 计算content的MD5;跟内存 CacheItem 中的md5做比较(第一次肯定不相等),如果不相等则将文件保存到磁盘中;



DiskUtil.saveToDisk(dataId, group, tenant, content);
  1. 如果MD5不相同,则更新 CacheItem 中的MD5属性和lastModifiedTs属性;lastModifiedTs是表示最后更新时间
  2. 如果MD5不相同,还要发送通知告知数据有变更;



EventDispatcher.fireEvent(new LocalDataChangeEvent(groupKey));

EventDispatcher是一个时间分发类,LocalDataChangeEvent是本地数据变更事件;他们的使用我在 文章 【Nacos源码 二】Nacos中的事件发布与订阅--观察者模式  中有比较详细的介绍; 现在我们主要来看下是哪个监听器收听了 LocalDataChangeEvent 事件; 通过反查代码我们找到 LongPollingService 这个监听类;是监听了这个事件的;

@Override
    public void onEvent(Event event) {
        if (isFixedPolling()) {
            // ignore
        } else {
            if (event instanceof LocalDataChangeEvent) {
                LocalDataChangeEvent evt = (LocalDataChangeEvent)event;
                scheduler.execute(new DataChangeTask(evt.groupKey, evt.isBeta, evt.betaIps));
            }
        }
    }

isFixedPolling()就是上面介绍过的SwitchService配置中的一个属性 isFixedPolling; 是否固定长轮询看代码最终执行的任务是 DataChangeTask ,这个任务做了数据更改之后的操作; 源码可以自己查看,这里就不列出来了,因为这里可以单独写一篇文章; 这里打上一个TODO... 后面回来补上; 简要概述这里的操作;

  1. 遍历所有的长轮询订阅者者(怎么订阅上的?后面会有文章介绍)
  2. 如果是beta发布且不在beta列表直接跳过
  3. 如果tag发布且不在tag列表直接跳过
  4. 发送Http请求通知所有未被上面2、3过滤掉的的订阅者最新的配置数据ConfigInfo

Nacos写入本地的Dump文件地址

在这里插入图片描述

ConfigService.updateMd5() 更新Md5并通知

这个方法就是上面 ConfigService.dump 的 3、4两个步骤

  1. 如果MD5不相同,则更新 CacheItem 中的MD5属性和lastModifiedTs属性;lastModifiedTs是表示最后更新时间
  2. 如果MD5不相同,还要发送通知告知数据有变更;

如果是快速启动,并且 isAllDump = false是如何Dump数据的

上面介绍的是全量Dump信息,那么如果我们启动的时候是isQuickStart=true;并且上一次心跳(也就是上一次服务正常的时候)距离现在不超过6小时;那么就不是全量Dump了;isAllDump = false;那么部分Dump是如何操作的呢? 有个执行器是 DumpChangeProcessor  ;这个执行器看名字就知道是 Dump有变化的数据的执行器,代码不看了;下面概述流程;

DumpChangeProcessor
  1. 查询所有的配置文件执行ConfigService.updateMd5(),这个方法做了什么看上面;其实就是将所有的配置文件缓存到内存中,并通知所有订阅的客户端
  2. 从his_config_info历史表中找到从上一次心跳时间(heartBeat.txt)到现在的所有被删除记录,his_config_info记录的就是历史的配置文件;
  3. 遍历2中的拿到的历史配置数据的dataId,group,Tenant;然后去config_info表中查找能不能查到数据
  • 如果能查到,说明配置不是被删除了,只是修改了content;
  • 如果不能查到,说明整个配置文件都被删除了;如果文件被删除了;则调用方法ConfigService.remove 将磁盘对应的配置文件删除;并且通知订阅的客户端数据变更;
  1. config_info表总查找  从上一次心跳时间(heartBeat.txt)到现在的所有有被修改过的配置数据,然后执行ConfigService.dumpChange 将这个改过的配置Dump的磁盘中,并通知;
  2. 然后load  Nacos内置的一些DataId配置; 上面提及的 ClientIpWhiteListAggrWhitelistSwitchService    ;

Dump所有的Beat配置DumpAllBetaProcessor

Dump所有的Tag配置DumpAllTagProcessor

上面两个基本是跟上面讲的都一样, Beat是灰度发布的配置; 将这些特殊的数据Dump到磁盘中,并且更新内存中的缓存;然后通知到那些 被配置了灰度发布的Ip白名单的订阅者;

Aggr 合并聚合数据

// add to dump aggr
            List<ConfigInfoChanged> configList = persistService.findAllAggrGroup();
            if (!CollectionUtils.isEmpty(configList)) {
                total = configList.size();
                List<List<ConfigInfoChanged>> splitList = splitList(configList, INIT_THREAD_COUNT);
                for (List<ConfigInfoChanged> list : splitList) {
                    MergeAllDataWorker work = new MergeAllDataWorker(list);
                    work.start();
                }
                log.info("server start, schedule merge end.");
            }

TODO.... 这里回头再详细说明

每隔十秒钟将当前时间保持到心跳文件中

 

TimerTaskService.scheduleWithFixedDelay(()-> heartbeat(), 0, 10, TimeUnit.SECONDS);

每隔6个小时全量Dump一次数据

//Full Dump every DUMP_ALL_INTERVAL_IN_MINUTE minutes
            TimerTaskService.scheduleWithFixedDelay(()-> dumpAllTaskMgr.addTask(DumpAllTask.TASK_ID, new DumpAllTask()) ,
                initialDelay, DUMP_ALL_INTERVAL_IN_MINUTE,
                TimeUnit.MINUTES);

每十分钟执行一次清空历史记录操作

his_config_info 存放的是历史记录,可以用于回滚操作; 每十分钟执行一次尝试删除操作; 删除的是超过N天的历史记录; 这个N是多少?默认是30天; 但是可以通过配置修改

nacos.config.retention.days=100

删除历史记录的方法

/**Delete the expiration history configuration record**/
    private void clearConfigHistory() {
        log.warn("clearConfigHistory start");
        if (ServerListService.isFirstIp()) {
            try {
                Timestamp startTime = getBeforeStamp(TimeUtils.getCurrentTime(), 24 * getRetentionDays());
                int totalCount = persistService.findConfigHistoryCountByTime(startTime);
                if (totalCount > 0) {
                    int pageSize = 1000;
                    int removeTime = (totalCount + pageSize - 1) / pageSize;
                    log.warn("clearConfigHistory, getBeforeStamp:{}, totalCount:{}, pageSize:{}, removeTime:{}",
                        new Object[] {startTime, totalCount, pageSize, removeTime});
                    while (removeTime > 0) {
                        // 分页删除,以免批量太大报错
                        persistService.removeConfigHistory(startTime, pageSize);
                        removeTime--;
                    }
                }
            } catch (Throwable e) {
                log.error("clearConfigHistory error", e);
            }
        }
    }

dumpConfigInfo()方法总结


光是dumpConfigInfo这一个方法就写了这么长的篇幅,我们给这个方法来做一个小结;

  • [ ] 全量Dump
  • 数据表config_info中查询所有的配置数据;
  • 加载Nacos内置的特殊配置数据ClientIpWhiteListAggrWhitelistSwitchService
    分别对应的 dataId= com.alibaba.nacos.metadata.aggrIDscom.alibaba.nacos.metadata.clientIpWhitelistcom.alibaba.nacos.meta.switch ;
  • 将所有配置文件保存到磁盘中;
  • 将所有配置文件缓存在内存中
  • 将配置文件通过Http通知到客户端中

满足下面这个的条件是

  1. 配置文件需要配置 isQuickStart=true2. 上一次服务心跳的时间;距离现在不超过6个小时
  • [ ] 快速启动,非全量Dump
  • 查询表config_info所有数据,将他们都缓存到内存中,并且通知到订阅的客户端,注意这个时候是没有Dump文件到磁盘的
  • 查询所有已经被删除的配置文件; 然后到本地磁盘中也把这些配置文件删除
  • 查询所有这段时间(上一次服务心跳的时间;距离现在的时间)有过更改的配置文件;然后把本地磁盘中的文件更新一下;
  • 加载Nacos内置的特殊配置数据ClientIpWhiteListAggrWhitelistSwitchService
    分别对应的 dataId= com.alibaba.nacos.metadata.aggrIDscom.alibaba.nacos.metadata.clientIpWhitelistcom.alibaba.nacos.meta.switch ;

问题

既然我们把数据Dump磁盘中,那么这么做的意义是什么呢? 它在哪里被用到了呢?

【Nacos源码之配置管理 五】为什么把配置文件Dump到磁盘中

相关文章
|
3月前
|
Cloud Native Java Nacos
微服务时代的新宠儿!Spring Cloud Nacos实战指南,带你玩转服务发现与配置管理,拥抱云原生潮流!
【8月更文挑战第29天】Spring Cloud Nacos作为微服务架构中的新兴之星,凭借其轻量、高效的特点,迅速成为服务发现、配置管理和治理的首选方案。Nacos(命名和配置服务)由阿里巴巴开源,为云原生应用提供了动态服务发现及配置管理等功能,简化了服务间的调用与依赖管理。本文将指导你通过五个步骤在Spring Boot项目中集成Nacos,实现服务注册、发现及配置动态管理,从而轻松搭建出高效的微服务环境。
272 0
|
27天前
|
数据管理 Nacos 开发者
"Nacos架构深度解析:一篇文章带你掌握业务层四大核心功能,服务注册、配置管理、元数据与健康检查一网打尽!"
【10月更文挑战第23天】Nacos 是一个用于服务注册发现和配置管理的平台,支持动态服务发现、配置管理、元数据管理和健康检查。其业务层包括服务注册与发现、配置管理、元数据管理和健康检查四大核心功能。通过示例代码展示了如何在业务层中使用Nacos,帮助开发者构建高可用、动态扩展的微服务生态系统。
74 0
|
1月前
|
Java Shell Nacos
升级Nacos竟然踩了这种坑?配置文件里的“隐形杀手”!
本文介绍了从Nacos 1.3.0升级到2.3.0的过程及注意事项,涵盖单机与集群模式的升级步骤,特别分享了一次因配置文件中多余空格导致的服务启动失败的经历,提醒读者注意配置细节。
56 0
|
4月前
|
Java 数据库连接 Nacos
nacos配置管理拉取不到配置异常
在搭建Nacos配置时遇到异常,因配置了`file-extension: yaml`,服务尝试拉取`shared-jdbc.yaml`, `shared-log.yaml`, `shared-swagger.yaml`,但Nacos中这些共享配置的Data ID无后缀。修正方法是确保Data ID与预期文件名一致,包括.yaml扩展名。在验证中,修改了部分Data ID并导致服务因找不到未加后缀的`jdbc`配置而报错,提示在配置Data ID时应包含文件扩展名。
199 1
|
3月前
|
关系型数据库 MySQL Java
“惊呆了!无需改动Nacos源码,轻松实现SGJDBC连接MySQL?这操作太秀了,速来围观,错过等哭!”
【8月更文挑战第7天】在使用Nacos进行服务治理时,常需连接MySQL存储数据。使用特定的SGJDBC驱动连接MySQL时,一般无需修改Nacos源码。需确保SGJDBC已添加至类路径,并在Nacos配置文件中指定使用SGJDBC的JDBC URL。示例中展示如何配置Nacos使用MySQL及SGJDBC,并在应用中通过Nacos API获取配置信息建立数据库连接,实现灵活集成不同JDBC驱动的目标。
118 0
|
5月前
|
Java Nacos 数据格式
Spring Cloud Nacos 详解:服务注册与发现及配置管理平台
Spring Cloud Nacos 详解:服务注册与发现及配置管理平台
213 3
|
5月前
|
开发框架 .NET Nacos
使用 Nacos 在 C# (.NET Core) 应用程序中实现高效配置管理和服务发现
使用 Nacos 在 C# (.NET Core) 应用程序中实现高效配置管理和服务发现
512 0
|
5月前
|
数据库连接 网络安全 Nacos
Nacos 的配置文件中修改了数据库的连接地址
Nacos 的配置文件中修改了数据库的连接地址
|
5月前
|
SpringCloudAlibaba 安全 Java
SpringCloudalibaba之Nacos的配置管理
如图所示,nacos-config-example被192.168.56.1获取过。
177 0
|
19天前
|
负载均衡 应用服务中间件 Nacos
Nacos配置中心
Nacos配置中心
50 1
Nacos配置中心
下一篇
无影云桌面