《一步一步看源码:Nacos》框架源码系列之一(其1,配置服务源码)

Nacos源码系列——第一章(Nacos核心源码主线剖析上) 在讲具体的源码之前,我有几点想说明下,很多开发可能觉得源码不重要,甚至觉得互联网 的知识,目前够用就可以,也不需要多么精通。的确,在大多数的公司中,你能用你的知识 解决问题就可以,不一定非要涉及到源码,但是你们应该知道如果想进大厂的话,对读源码 的能力是非常高的,甚至像阿里,字节这样的厂,面试经常会问到,尤其是做中间件组件, 对开源框架的源码阅读能力,是有一定的要求的,那么想熟悉源码的过程和思想,可以看看 这篇,会让你受益匪浅!! 1、为什么要看源码 我总结有以下几点,个人觉得非常重要的. 阅读详情

Nacos源码

​ 因为最近项目在做容器化处理,容器化后涉及到不同进程对同一个文件的读写,考虑到可能会存在同一文件的配置文件,可能会把彼此覆盖掉,所以这里学习一下Nacos源码。

整体结构图

​ 这边主要看配置中心、服务注册中心的源码,其他也会说,但不会那么细致。

在这里插入图片描述

源码解析

配置服务源码

​ 这一块源码,我们从nacos的测试的demo入手进行学习,先总概整体过程再拆分出来一个个剖析。

  • 先找到ConfigExample,配置服务示例(建议起一个Nacos服务,对着学源码),重点看下以下代码
    • 获取配置文件过程:使用nacos地址—>获取配置对象—>使用配置对象、文件名、组名获取配置信息
    • 设置监听器:使用配置对象、文件名、组名配置—>必须重写两方法
    • 更新配置信息:使用配置对象,把文件名、组名给上即可
    • 删除配置:使用配置对象,把文件名、组名给上即可
public class ConfigExample {
    public static void main(String[] args) throws NacosException, InterruptedException {
        //*************获取配置信息****************
        String serverAddr = "localhost";  //设置nacos的地址
        String dataId = "test"; //文件名或者叫id也可以
        String group = "DEFAULT_GROUP"; //对应的组
        Properties properties = new Properties();//创造一个配置类
        properties.put("serverAddr", serverAddr);//将nacos服务地址设置进去
        //使用Nacos的ip地址,创建nacos配置服务对象(后面细说)
        ConfigService configService = NacosFactory.createConfigService(properties);
       //从配置服务对象获取配置内容(传入文件名、组名、超时时间)
        String content = configService.getConfig(dataId, group, 5000);
        System.out.println(content); //拿到的配置内容
       	//*************增加服务配置的监听器****************
        configService.addListener(dataId, group, new Listener() {
            //输出配置信息
            @Override
            public void receiveConfigInfo(String configInfo) {
                System.out.println("receive:" + configInfo);
            }
           
            //可以获取线程,当坚挺到配置的时候,执行某些任务
            @Override
            public Executor getExecutor() {
                return null;
            }
        });
		//*************推送新配置信息****************
        //判断推送是否成功
        boolean isPublishOk = configService.publishConfig(dataId, group, "content");
        System.out.println(isPublishOk);
		
        //*************获取新的配置信息****************
        Thread.sleep(3000);
        content = configService.getConfig(dataId, group, 5000);
        System.out.println(content);
		
        //*************获取新的配置信息****************
        boolean isRemoveOk = configService.removeConfig(dataId, group);
        System.out.println(isRemoveOk);
        Thread.sleep(3000);
		
        //再次获取配置信息确保已经删除
        content = configService.getConfig(dataId, group, 5000);
        System.out.println(content);
        Thread.sleep(300000);
    }
}

下面把上面的部分拆开细讲。

获取配置文件
  • ctrl+鼠标左键点击getConfig进入源码查看
        Thread.sleep(3000);
        content = configService.getConfig(dataId, group, 5000);
        System.out.println(content);
  • 点击查看实现类

在这里插入图片描述

  • 进入getConfigInner进去继续查看
    @Override
    public String getConfig(String dataId, String group, long timeoutMs) throws NacosException {
        return getConfigInner(namespace, dataId, group, timeoutMs);
    }
  • 开始剖析getConfigInner,直接看注释即可
    • 特别注意下:tenant和namespace是一个东西,从nacos的显示的日志可以看出。
    • 整个过程如下:
  • 先从本地磁盘中加载配置,因为应用在启动时,会加载远程配置缓存到本地,如果本地文件的内容不为空,直接返回。
  • 如果本地文件的内容为空,则调用worker.getServerConfig加载远程配置
  • 如果出现异常,则调用本地快照文件加载配置
    private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException {
        //判断group是否为空,为空则设为默认
        group = blank2defaultGroup(group);
        //校验dataId、group保证不为空
        ParamUtils.checkKeyParam(dataId, group);
        //创建配置响应对象
        ConfigResponse cr = new ConfigResponse();
        
    	//设置文件id、命名空间、组
        cr.setDataId(dataId);
        cr.setTenant(tenant);
        cr.setGroup(group);
        
        //优先加载本地配置
        String content = LocalConfigInfoProcessor.getFailover(worker.getAgentName(), dataId, group, tenant);
        if (content != null) {
            //如果本地内容不为空,则告知从本地加载成功(后面细说)
            LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}, config={}",
                    worker.getAgentName(), dataId, group, tenant, ContentUtils.truncateContent(content));
           //将加载到的内容放入配置对象
            cr.setContent(content);
            //获取容灾配置的EncryptedDataKey
            String encryptedDataKey = LocalEncryptedDataKeyProcessor
                    .getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);
            //放入容灾配置的EncryptedDataKey
            cr.setEncryptedDataKey(encryptedDataKey);
            //过滤链
            configFilterChainManager.doFilter(null, cr);
            //从响应对象重新获取配置内容
            content = cr.getContent();
            //返回配置内容
            return content;
        }
        
        try {
            //加载远程配置,获取配置对象(后面细说)
            ConfigResponse response = worker.getServerConfig(dataId, group, tenant, timeoutMs, false);
            //从远程配置里获取配置内容,并设置
            cr.setContent(response.getContent());
            //从配置响应对象获取EncryptedDataKey,并设置EncryptedDataKey
            cr.setEncryptedDataKey(response.getEncryptedDataKey());
            //过滤链
            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={}",
                    worker.getAgentName(), dataId, group, tenant, ioe.toString());
        }
        
        LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}, config={}",
                worker.getAgentName(), dataId, group, tenant, ContentUtils.truncateContent(content));
        //从快照中获取(后面细说,在快照加载部分)
        content = LocalConfigInfoProcessor.getSnapshot(worker.getAgentName(), dataId, group, tenant);
        //放入获取到的配置内容
        cr.setContent(content);
        //获取encryptedDataKey
        String encryptedDataKey = LocalEncryptedDataKeyProcessor
                .getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);
        //向配置响应对象里放入encryptedDataKey
        cr.setEncryptedDataKey(encryptedDataKey);
        //过滤链
        configFilterChainManager.doFilter(null, cr);
        //重新获取配置内容
        content = cr.getContent();
        //返回配置内容
        return content;
    }

本地配置加载
  • 从getFailover进入查看本地配置源码
        // use local config first
        String content = LocalConfigInfoProcessor.getFailover(worker.getAgentName(), dataId, group, tenant);
  • 进入getFailover查看
    • 获取本地配置地址,根据命名空间进行拼串
      • 没有命名空间:/${serverName}_nacos/data/config-data
      • 存在命名空间:/serverNamenacos/data/config−data−tenant/{serverName}_nacos/data/config-data-tenant/serverNamen​acos/data/config−data−tenant/{tenant}
    • 进入readFile,继续查看
    public static String getFailover(String serverName, String dataId, String group, String tenant) {
        File localPath = getFailoverFile(serverName, dataId, group, tenant);
        if (!localPath.exists() || !localPath.isFile()) {
            //如果文件不存在则返空
            return null;
        }        
        try {
            //读取文件
            return readFile(localPath);
        } catch (IOException ioe) {
            LOGGER.error("[" + serverName + "] get failover error, " + localPath, ioe);
            return null;
        }
    }
  • 进入readFile方法继续查看
    protected static String readFile(File file) throws IOException {
        if (!file.exists() || !file.isFile()) {
            //判断文件路径对应的文件是否存在,不存在则返空
            return null;
        }
        //判断是否为多实例,多实例则采取文件锁获取
        if (JvmUtil.isMultiInstance()) {
            return ConcurrentDiskUtil.getFileContent(file, Constants.ENCODE);
        } else {//否则使用正常的文件打开方式读取配置即可
            try (InputStream is = new FileInputStream(file)) {
                return IoUtils.toString(is, Constants.ENCODE);
            }
        }
    }
  • 进入getFileContent方法查看
    public static String getFileContent(File file, String charsetName) throws IOException {
        RandomAccessFile fis = null; //创建一个随机流
        FileLock rlock = null;	//创建一个文件锁
        try {
            //因为只读取配置,所以权限为“只读”
            fis = new RandomAccessFile(file, READ_ONLY);
            FileChannel fcin = fis.getChannel();
            int i = 0;
            do {
                try {
                    //尝试获取该文件的文件锁,如果获取不到则返回null
                    rlock = fcin.tryLock(0L, Long.MAX_VALUE, true);
                } catch (Exception e) {
                    //没有获取到则抛出异常
                    ++i;
                    //如果尝试10次还获取不到锁则抛出异常
                    if (i > RETRY_COUNT) {
                        LOGGER.error("read {} fail;retryed time:{}", file.getName(), i);
                        throw new IOException("read " + file.getAbsolutePath() + " conflict");
                    }
                    //休眠10ms*次数
                    sleep(SLEEP_BASETIME * i);
                    LOGGER.warn("read {} conflict;retry time:{}", file.getName(), i);
                }
            } while (null == rlock);//自旋锁,一直尝试获取锁
            //获取当前通道大小
            int fileSize = (int) fcin.size();
            //创建缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocate(fileSize);
            //把字节读入
            fcin.read(byteBuffer);
            //刷新缓冲区
            byteBuffer.flip();
            //将缓冲区字节转换为字符串
            return byteBufferToString(byteBuffer, charsetName);
        } finally {
            if (rlock != null) {
                //释放锁
                rlock.release();
                rlock = null;
            }
            if (fis != null) {
               //关闭流
                IoUtils.closeQuietly(fis);
                fis = null;
            }
        }
    }

远程配置加载

​ 触发远程配置加载的情况是,本地配置文件为空,则会从远程中心去调用:

    public ConfigResponse getServerConfig(String dataId, String group, String tenant, long readTimeout, boolean notify)
            throws NacosException {
        if (StringUtils.isBlank(group)) {
        	//判断组是否为空,为空则使用默认组名
            group = Constants.DEFAULT_GROUP;
        } 
        	//使用配置id、组名、命名空间、超时时间和是否使用缓存
        return this.agent.queryConfig(dataId, group, tenant, readTimeout, notify);
    }
  • 进入queryConfig方法继续查看
    • 如果notify为true,则会从缓存里找到对应的缓存数据,使用缓存数据去获取先前加载过这个配置的客户端。
        @Override
        public ConfigResponse queryConfig(String dataId, String group, String tenant, long readTimeouts, boolean notify)
                throws NacosException {
            //使用文件id、组名、命名空间生成请求
            ConfigQueryRequest request = ConfigQueryRequest.build(dataId, group, tenant);
            //把notify放入请求的头部信息中
            request.putHeader(NOTIFY_HEADER, String.valueOf(notify));
            //以0位id,获取一个正在运行的客户端
            RpcClient rpcClient = getOneRunningClient();
            if (notify) {
            //查看notify是否为true,为true则从缓存里获取对应客户端
                CacheData cacheData = cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant));
                if (cacheData != null) {
                    //如果缓存不为空,则获取数据对应存储的客户端使用
                    rpcClient = ensureRpcClient(String.valueOf(cacheData.getTaskId()));
                }
            }
            //获取查询响应对象
            ConfigQueryResponse response = (ConfigQueryResponse) requestProxy(rpcClient, request, readTimeouts);
            //创建配置对象
            ConfigResponse configResponse = new ConfigResponse();
            if (response.isSuccess()) {
                //如果响应成功,则将其存储到快照中(执行逻辑:拿组名、命名空间、文件id去查询)(细说saveSnapshot)
                //1、首先获取快照文件(是否存在命名空间)	
                // 不存在命名空间:/config_rpc_client_nacos/snapshot/${group}/${dataId}
                //	存在命名空间:/config_rpc_client_nacos/snapshot-tenant/${tenant}/${group}/${dataId}
                LocalConfigInfoProcessor.saveSnapshot(this.getName(), dataId, group, tenant, response.getContent());
                //将文件内容置入
                configResponse.setContent(response.getContent());
                //初始化配置类型
                String configType;
                //判断响应实体里的文本类型是否为空
                if (StringUtils.isNotBlank(response.getContentType())) {
                    //不为空则从响应体获取文件类型
                    configType = response.getContentType();
                } else {
                    //如果响应体没有设置文件类型,则设置为TEXT
                    configType = ConfigType.TEXT.getType();
                }
                	//将配置文件类型给上
                configResponse.setConfigType(configType);
                	//获取encryptedDataKey(理解成一个秘钥就可以)
                String encryptedDataKey = response.getEncryptedDataKey();
                	//保存快照,内容为空则删除
                LocalEncryptedDataKeyProcessor
                        .saveEncryptDataKeySnapshot(agent.getName(), dataId, group, tenant, encryptedDataKey);
               		//把encryptedDataKey设置进去
                configResponse.setEncryptedDataKey(encryptedDataKey);
                	//返回响应结果
                return configResponse;
                //错误码300,此时配置为null,删除同名的快照,如果快照不存在,则报错。(细说saveSnapshot)
            } else if (response.getErrorCode() == ConfigQueryResponse.CONFIG_NOT_FOUND) {
                LocalConfigInfoProcessor.saveSnapshot(this.getName(), dataId, group, tenant, null);
                LocalEncryptedDataKeyProcessor.saveEncryptDataKeySnapshot(agent.getName(), dataId, group, tenant, null);
                return configResponse;
                //错误码400,直接抛异常
            } else if (response.getErrorCode() == ConfigQueryResponse.CONFIG_QUERY_CONFLICT) {
                LOGGER.error(
                        "[{}] [sub-server-error] get server config being modified concurrently, dataId={}, group={}, "
                                + "tenant={}", this.getName(), dataId, group, tenant);
                throw new NacosException(NacosException.CONFLICT,
                        "data being modified, dataId=" + dataId + ",group=" + group + ",tenant=" + tenant);
            } else {
                //其他的,也直接抛异常
                LOGGER.error("[{}] [sub-server-error]  dataId={}, group={}, tenant={}, code={}", this.getName(), dataId,
                        group, tenant, response);
                throw new NacosException(response.getErrorCode(),
                        "http error, code=" + response.getErrorCode() + ",msg=" + response.getMessage() + ",dataId=" + dataId + ",group=" + group
                                + ",tenant=" + tenant);
                
            }
        }
  • 细说一下快照存储的过程
    • 不存在命名空间:/config_rpc_client_nacos/snapshot/group/{group}/group/{dataId}
    • 存在命名空间:/config_rpc_client_nacos/snapshot-tenant/tenant/{tenant}/tenant/{group}/${dataId}
    public static void saveSnapshot(String envName, String dataId, String group, String tenant, String config) {
        //查看是否开启快照功能,没有则直接返回
        if (!SnapShotSwitch.getIsSnapShot()) {
            return;
        }
        //获取路径
        //不存在命名空间:/config_rpc_client_nacos/snapshot/${group}/${dataId}
		//存在命名空间:/config_rpc_client_nacos/snapshot-tenant/${tenant}/${group}/${dataId}
        File file = getSnapshotFile(envName, dataId, group, tenant);
        if (null == config) { //如果传入的配置文件内容为空
            try {
                //删除这个配置文件
                IoUtils.delete(file);
            } catch (IOException ioe) {
                LOGGER.error("[" + envName + "] delete snapshot error, " + file, ioe);
            }
        } else {
            try {
                //获取父类目录
                File parentFile = file.getParentFile();
                //如果父类目录不存在
                if (!parentFile.exists()) {
                    //新建目录
                    boolean isMdOk = parentFile.mkdirs();
                    //如果新建目录失败
                    if (!isMdOk) {
                    //新建目录出错
                        LOGGER.error("[{}] save snapshot error", envName);
                    }
                }
                
                if (JvmUtil.isMultiInstance()) {
                    //如果是多实例,使用文件锁将配置文件写入文件(下面细说)
                    ConcurrentDiskUtil.writeFileContent(file, config, Constants.ENCODE);
                } else {
                   //如果是单实例,直接写入
                    IoUtils.writeStringToFile(file, config, Constants.ENCODE);
                }
            } catch (IOException ioe) {
                LOGGER.error("[" + envName + "] save snapshot error, " + file, ioe);
            }
        }
    }
  • 进入writeFileContent进行查看
    public static Boolean writeFileContent(File file, String content, String charsetName) throws IOException {
        //如果文件不存在
        if (!file.exists()) {
            //创建文件,返回失败结果
            boolean isCreateOk = file.createNewFile();
            if (!isCreateOk) {
                return false;
            }
        }
        //初始化信道,文件锁,随机流
        FileChannel channel = null;
        FileLock lock = null;
        RandomAccessFile raf = null;
        try {
            //随机流读取文件,给与读和写的权限
            raf = new RandomAccessFile(file, READ_WRITE);
            //获取信道
            channel = raf.getChannel();
            //初始化获取锁的次数
            int i = 0;
            do {
                try {
                    //尝试获取锁
                    lock = channel.tryLock();
                } catch (Exception e) {
                    ++i;//获取锁的次数+1
                    //获取次数高于10次报错
                    if (i > RETRY_COUNT) {
                        LOGGER.error("write {} fail;retryed time:{}", file.getName(), i);
                        throw new IOException("write " + file.getAbsolutePath() + " conflict");
                    }
                    //休眠,防止进程里的线程占用过高,导致cpu爆满
                    sleep(SLEEP_BASETIME * i);
                    LOGGER.warn("write {} conflict;retry time:{}", file.getName(), i);
                }
            } while (null == lock);
            //将配置文件内容压到缓冲区
            ByteBuffer sendBuffer = ByteBuffer.wrap(content.getBytes(charsetName));
            //将其通过信道写入
            while (sendBuffer.hasRemaining()) {
                channel.write(sendBuffer);
            }
            channel.truncate(content.length());
        } catch (FileNotFoundException e) {
            throw new IOException("file not exist");
        } finally {
            if (lock != null) {
                try {
                    lock.release();
                    lock = null;
                } catch (IOException e) {
                    LOGGER.warn("close wrong", e);
                }
            }
            if (channel != null) {
                try {
                    channel.close();
                    channel = null;
                } catch (IOException e) {
                    LOGGER.warn("close wrong", e);
                }
            }
            if (raf != null) {
                try {
                    raf.close();
                    raf = null;
                } catch (IOException e) {
                    LOGGER.warn("close wrong", e);
                }
            }
            
        }
        return true;
    }
使用快照加载
  • 从getSnapshot进入快照获取配置文件的源码中
    public static String getSnapshot(String name, String dataId, String group, String tenant) {
        //如果没有开启快照,则直接返空
        if (!SnapShotSwitch.getIsSnapShot()) {
            return null;
        }
		//获取路径
        //不存在命名空间:/config_rpc_client_nacos/snapshot/${group}/${dataId}
        //存在命名空间:/config_rpc_client_nacos/snapshot-tenant/${tenant}/${group}/${dataId}
        File file = getSnapshotFile(name, dataId, group, tenant);
        //如果文件不存在或者不是文件,则直接返空
        if (!file.exists() || !file.isFile()) {
            return null;
        }
        try {
            //读文件入口
            return readFile(file);
        } catch (IOException ioe) {
            LOGGER.error("[" + name + "]+get snapshot error, " + file, ioe);
            return null;
        }
    }

  • readFile这个方法讲过了,返回本地配置加载的readFile方法查看即可。

至此,Nacos的配置文件获取源码就讲完了。

删除配置文件
  • 删除配置文件的入口
boolean isRemoveOk = configService.removeConfig(dataId, group);
  • 一路追踪到removeConfig

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-25wbvvso-1656468857038)(nacos源码学习/image-20220120161125426.png)]

  • 查看配置移除的代码
        @Override
        public boolean removeConfig(String dataId, String group, String tenant, String tag) throws NacosException {
            //新建一个删除配置请求的实体,传入文件Id,组名、命名空间
            ConfigRemoveRequest request = new ConfigRemoveRequest(dataId, group, tenant, tag);
            //使用网关发起请求
            ConfigRemoveResponse response = (ConfigRemoveResponse) requestProxy(getOneRunningClient(), request);	
            //返回响应结果
            return response.isSuccess();
        }
更新配置文件
  • 从先前的demo一路追到publishConfig

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4jn82D5y-1656468857039)(nacos源码学习/image-20220120162428117.png)]

  • 进入publishConfig查看
    private boolean publishConfigInner(String tenant, String dataId, String group, String tag, String appName,
            String betaIps, String content, String type, String casMd5) throws NacosException {
        //查看组名是否为空,为空则使用DEFAULT_GROUP为默认组名
        group = blank2defaultGroup(group);
        //校验文件id,组名和配置文件内容
        ParamUtils.checkParam(dataId, group, content);
        //初始化配置文件实体
        ConfigRequest cr = new ConfigRequest();
        //放入文件id、组名、命名空间、类型等
        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的publishConfig一路追到代理的实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QZm87yD3-1656468857040)(nacos源码学习/image-20220120163230662.png)]

  • 进入publishConfig查看
        @Override
        public boolean publishConfig(String dataId, String group, String tenant, String appName, String tag,
                String betaIps, String content, String encryptedDataKey, String casMd5, String type)
                throws NacosException {
            try {
                //将要更新的内容,文件id,组名,命名空间放入请求中
                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);
                //使用网关发送请求
                ConfigPublishResponse response = (ConfigPublishResponse) requestProxy(getOneRunningClient(), request);
                //请求如果不成功返回false
                if (!response.isSuccess()) {
                    LOGGER.warn("[{}] [publish-single] fail, dataId={}, group={}, tenant={}, code={}, msg={}",
                            this.getName(), dataId, group, tenant, response.getErrorCode(), response.getMessage());
                    return false;
                } else {
                    //成功返回true
                    LOGGER.info("[{}] [publish-single] ok, dataId={}, group={}, tenant={}, config={}", getName(),
                            dataId, group, tenant, ContentUtils.truncateContent(content));
                    return true;
                }
            } catch (Exception e) {
                LOGGER.warn("[{}] [publish-single] error, dataId={}, group={}, tenant={}, code={}, msg={}",
                        this.getName(), dataId, group, tenant, "unkonw", e.getMessage());
                return false;
            }
        }
Nacos——注册中心源码详解 基于Nacos的1.4.3版本,针对Nacos注册中心的服务注册、心跳机制、服务发现三大核心模块做一个源码上的流程详解 阅读详情

相关推荐

一、Nacos源码系列:源码环境搭建

使用git clone https://github.com/alibaba/nacos命令将源码clone下来,或者先fork到自己的GitHub,然后clone到本地,这样阅读的时候,可以添加一些注释,提交到自己的GitHub上面,方便后续再次回来查看时快速回想起代码的含义。至此,Nacos源码阅读的环境就搭建好了,后续我们会进行服务注册、服务发现、以及服务心跳等功能进行分析。截止到编写这篇文章,Nacos的最新版本为2.3.0,为了学习,我们也是拿最新的源码来学习。

weixiaohuai的博客 2874

Nacos配置服务的源码解析(全)

Nacos配置服务的源码解析 从Nacos的源代码分析Nacos Cofnig的实现 NacosConfigAutoConfiguration 负责Nacos配置服务的自动配置 如果spring.cloud.nacos.config.enabled(默认为true)为false时因为ConditionalOnProperty注解不导入到Spring Factory。 装配的Bean: NacosConfigProperties:Nacos配置文件POJO,负责通过Spring的配置功能从配置文件中加载

a2632699773的博客 1万+

二、Nacos源码系列:Nacos服务注册流程(一)

本篇文章中,我们从Nacos客户端自动注册在何时触发开始,然后分析了客户端向Nacos服务端提交服务注册请求,最后到Nacos服务端处理这个注册请求,整个流程大概梳理了一下,下一篇文章,我们将会详细分析NotifyCenter这个事件-发布机制的原理,然后继续分析服务端处理注册请求过程中涉及到的后续流程处理。

weixiaohuai的博客 3802

Nacos服务注册源码解析

简介:本文将以图文方式详述 Spring Cloud Alibaba 技术体系之 Nacos 服务注册源码解析。

Hyatt的博客 2678

微服务系列(二)(5) Nacos源码分析

微服务系列(二)(5) Nacos源码分析 Nacos (official site: http://nacos.io) is an easy-to-use platform designed for dynamic service discovery and configuration and service management. It helps you to build cloud na...

XCXCXCXCX 1万+

Nacos2.X源码分析:服务注册、服务发现流程

方法中会判断当前要注册的实例是否为临时实例,如果是临时实例就使用grpc的方式请求NacosService,如果是持久化实例就还是使用http的方式请求,一般情况下都是临时实例,所以会采用grpc的方式调用。至此,NacosClient端请求NacosServer端进行服务注册的一次请求就结束了,但NacosServer端真正处理的逻辑还没有结束,接下来要去看监听事件是如何进行处理的。进行全文搜索,看看某个事件对象到底是在哪里进行处理的。处理该事件的位置和处理服务注册的位置是一样的,都是。

qq_44027353的博客 2354

SpringCloudAlibaba微服务远程调用之Nacos篇—Nacos的安装、服务注册、服务集群配置、配置负载均衡策略、配置命名空间与非临时实例、nacos集群配置、nacos与feign的结合

本文主要是通过结合实例进行针对nacos的安装,项目、环境、服务集群。nacos集群的配置,以及如何使用nacos并通过RestTemplate或者使用openfeign实现服务的远程调用,废话不多说,直接开始一、基础项目导入。

weixin_64339548的博客 3591

微服务系列之Nacos注册中心源码解读

源码下载地址:https://github.com/alibaba/nacos 从官网架构图中可以看出nacos内部提供了nacos-namign和nacos-config两个服务,作为注册中心和配置中心,nacos-core作为nacos-naming和nacos-config两个模块的公共支持部分,提供了一些相关工具类 Nacos通过提供一系列的http接口来提供Naming服务...

qq_41967563的博客 1495

Nacos源码分析

Nacos源码分析 1.下载Nacos源码并运行 要研究Nacos源码自然不能用打包好的Nacos服务端jar包来运行,需要下载源码自己编译来运行。 1.1.下载Nacos源码 Nacos的GitHub地址:https://github.com/alibaba/nacos 课前资料中已经提供了下载好的1.4.2版本的Nacos源码: 如果需要研究其他版本的同学,也可以自行下载: 大家找到其release页面:https://github.com/alibaba/nacos/tags,找到其中的1.4.2.

学过的东西,讲不出来等于没学。 1万+

【Nacos源码之配置管理 一】阅读源码第一步,本地启动Nacos

前言 在我们去阅读Nacos源码之前,我们得先了解Nacos是干嘛的,以及怎么用,这样有利于我们更容易去理解源码; 查看Nacos的官方文档,我们知道Naocs主要有一下特性: 配置中心 服务注册与发现 … Nacos有给我们提供管理界面,用来方便配置数据;我们先把整个Nacos源码克隆下来,本地启动;熟悉一下如何使用它 启动Nacos项目 1.克隆Nacos源码 git clone http...

石臻说AI(szzdzhp001) 【原名 石臻臻的杂货铺】 1万+

基于FreeSwitch与SIP UA的蓝牙电话坐席系统实战部署

本文详细介绍了如何基于FreeSwitch开源软交换平台,结合SIP UA软电话与蓝牙电话App,在局域网内搭建一套低成本、自主可控的电话坐席系统。文章提供了从环境准备、FreeSwitch安装配置、用户与拨号计划创建,到SIP软电话测试和蓝牙设备接入的完整实战部署流程,并给出了关键优化与排坑指南,适合小微团队或个人开发者构建内部通讯工具。

zeta9的博客 544

七、Nacos源码系列:Nacos服务发现

/ 调用栈如下:// getInstances(serviceId)方法最终会调用NacosNamingService#selectInstances()获取实例信息。// 集群名称,使用逗号分隔// 是否订阅,默认是订阅的/*** 1.从缓存中获取ServiceInfo* key: groupName@@serviceName 或者 groupName@@serviceName@@clusterString*/

weixiaohuai的博客 4963

PowerBuilder 9 遗留系统逆向实战:3款反编译工具对比与关键DLL破解

本文深入探讨了PowerBuilder 9遗留系统的逆向工程实战,对比分析了PBKiller、PB DeCompiler和Shudepb三款反编译工具的核心功能与适用场景,并详细解析了关键DLL破解技术。通过IDA和OllyDbg等工具的应用,提供了从静态解构到动态追踪的完整逆向方法论,帮助开发者有效维护老旧PB系统。

cucanqi9387的博客 1379

十四、Nacos源码系列:Nacos配置发布原理

一般情况下,我们是通过Nacos提供的web控制台登录,然后通过界面新增配置信息。后续客户端只要配置了对应的NameSpace,group,dataId就可以在客户端获取到对应的配置信息。既然这样,服务端肯定会存储我们在web控制台配置的配置信息。web控制台发布配置的入口肯定也是一个controller接口:com.alibaba.nacos.config.server.controller.ConfigController#publishConfig。// 内容加密} else {

weixiaohuai的博客 2301

Eclipse在Mac上报错JNI_CreateJavaVM?可能是你的JDK架构搞错了(附Adoptium JDK下载指南)

本文深入解析了Mac(尤其是Apple Silicon芯片)上Eclipse启动时出现“JNI_CreateJavaVM”错误的根本原因,即JDK架构(aarch64与x86_64)不匹配。文章提供了详细的诊断步骤,并推荐通过下载并配置Eclipse Adoptium提供的aarch64版本JDK来彻底解决问题,同时附上清晰的配置指南。

weixin_29065659的博客 321

Transformer入门(一)——结构

Transformer入门介绍篇,分别介绍了Transformer的总体结构与每个模块的实现原理

yeen123的博客 9424

服务注册与配置:Nacos

目录第一章 Nacos的介绍1.1、Nacos是什么1.2、Nacos的特性第二章 Nacos安装与运行2.1、下载Nacos2.2、解压Nacos2.3、运行Nacos2.4、访问Nacos第三章 Nacos作注册中心3.1、服务提供者3.1.1、创建工程3.1.2、导入依赖3.1.3、编写配置3.1.4、主启动类3.1.5、控制器类3.1.6、启动测试3.1.7、服务集群3.2、服务消费者3.2.1、创建工程3.2.2、导入依赖3.2.3、编写配置3.2.4、主启动类3.2.5、控制器类3.2.6、启动

轻松的小希 7067

绕过App某加密企业版加固Frida检测

可以发现dlsym函数加载了两次pthread_create,可以认为这两个进程(或某一个进程)是和Frida的检测有关的,于是为了绕过检测,我们可以让它加载一个虚假的pthread_create函数,得到一个空结果绕过检测。成功绕过了libnllvm1717052779924.so的检测,又到了libface_nllvm.so的检测,使用同样的方法去绕过libface_nllvm.so的检测,代码我这里就不上了,大家可以自己修改以下。dlsym()函数的作用是在已经加载的动态链接库中查找指定符号的地址。

墨痕诉清风的博客 1621

【Nacos源码系列】服务注册的原理

Nacos是一个基于云原生的动态服务发现、配置管理和服务治理平台,由阿里巴巴开源。它提供了服务注册与发现、配置管理、动态DNS、流量管理、服务降级、负载均衡、限流、路由管理等一系列核心功能,可以帮助企业构建弹性可扩展的微服务架构。

做一个有思想的技术人 1628

SAP FICO CBS接口-银企直连付款平台功能开发说明书(包括测试样例、FS)

SAP FICO CBS接口-银企直连付款平台功能开发说明书(包括测试样例、FS)

我好像病了的博客 2448

5G射频到底是啥?方案怎么搭?工作流程、SAW选型、PA选型一口气讲透(工程师秒懂版)

很多人一提 5G 射频,脑子里先冒出来的是几个零件名:👉 PA👉 LNA👉 SAW👉 天线👉 开关但真正做方案时,这些东西不是一堆分立器件,而是一条完整的“信号流水线”。更准确地说,。Qualcomm 对 RFFE 的定义就是“把信息转换成可发送的射频信号,并把接收到的信号转换回有用信息”的那一整套组件。

weixin_43199439的博客 533

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

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

cnzzs的博客 1651

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

从源码角度一步步窥探Nacos配置拉取与动态刷新、监听原理

在分析原理前,我们先来回顾一些nacos的几个用法。 用法一:读取nacos配置中心的值。这种语法跟注入本地的配置一样。是spring的注解,从当前环境的属性集合获取值。 @Value(value = "${info:Local Hello world}") private String info; 配置中心的值变化后,应用无法实时感知,需要重启应用,值才会相应变更。 用法二:动态获取nacos配置中心的值。用的是nacos的注解NacosValue,autoRefreshed 表示是否自动更新。 @

啊杰的博客 7259

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

nacos2.1.1版本,作为注册中心,配置中心如何使用,nacos服务端启动,微服务搭建,nacos集群化部署

nacos2.1.1版本,作为注册中心,配置中心如何使用,nacos服务端启动,微服务搭建,nacos集群化部署

lpg432的博客 3461

ssm框架搭建

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

【Nacos配置中心】对配置文件内容进行加密并构建nacos容器

我们有时候会在配置文件存储很多敏感信息,出于此考虑,Nacos在2.1版本新发布了配置文件加密功能,根本不需要我们自己来实现,很轻松的就能实现

onetoone3的博客 3555
上一篇: virtualbox扩容的两种方法
下一篇: 《一步一步看源码:Nacos》框架源码系列之一(其二,服务注册源码)
年轻的猴
博客等级 码龄8年 58粉丝 343原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值