【深入理解SpringCloud微服务】深入理解nacos配置中心(三)——服务端启动与获取配置源码分析

【深入理解SpringCloud微服务】深入理解nacos配置中心(三)——服务端启动与获取配置源码分析

原理回顾

服务端启动

我们在《宏观理解nacos配置中心原理》的文章说到了,服务端启动的原理。

在这里插入图片描述

会调用DumpService查询MySQL,然后把查询到的配置信息dump到磁盘成为一个个的配置文件,每个DataId对应一个配置文件。

获取配置

然后服务端接收到获取配置文件的请求时,从磁盘中查询对应文件返回,而不是去查询数据库。

在这里插入图片描述

源码分析

服务端启动

ExternalDumpService#init()

我们以nacos配置的数据库是MySQL的情况为例子,DumpService的实现类就是ExternalDumpService。

nacos配置中心其实就是一个SpringBoot应用,它的启动就是SpringBoot启动。然后由于ExternalDumpService的init()方法被@PostConstruct注解修饰,因此在初始化ExternalDumpService时init()方法会被调用。

    @PostConstruct
    @Override
    protected void init() throws Throwable {
        dumpOperate(processor, dumpAllProcessor, dumpAllBetaProcessor, dumpAllTagProcessor);
    }

在这里插入图片描述

    protected void dumpOperate(DumpProcessor processor, DumpAllProcessor dumpAllProcessor,
            DumpAllBetaProcessor dumpAllBetaProcessor, DumpAllTagProcessor dumpAllTagProcessor) throws NacosException {
        ...
                dumpConfigInfo(dumpAllProcessor);
       ...
    }
   private void dumpConfigInfo(DumpAllProcessor dumpAllProcessor) throws IOException {
        ...
                dumpAllProcessor.process(new DumpAllTask());
            ...
    }

在这里插入图片描述

经过一轮调用,进入到DumpAllProcessor#process方法。

    @Override
    public boolean process(NacosTask task) {
    	// 查询最大的id值
        long currentMaxId = persistService.findConfigMaxId();
        long lastMaxId = 0;
        while (lastMaxId < currentMaxId) {
        	// 分页查询MySQL
            Page<ConfigInfoWrapper> page = persistService.findAllConfigInfoFragment(lastMaxId, PAGE_SIZE);
            if (page != null && page.getPageItems() != null && !page.getPageItems().isEmpty()) {
                for (ConfigInfoWrapper cf : page.getPageItems()) {
                	long id = cf.getId();
                    lastMaxId = Math.max(id, lastMaxId);
                    ...
                    // 调用ConfigCacheService把查询到的每一条记录dump到磁盘成一个配置文件
                    ConfigCacheService.dump(cf.getDataId(), cf.getGroup(), cf.getTenant(), cf.getContent(),
                            cf.getLastModified(), cf.getType(), cf.getEncryptedDataKey());
                    
                    ...
                }
                ...
            }
            ...
        }
        return true;
    }

在这里插入图片描述

ConfigCacheService#dump()

    public static boolean dump(String dataId, String group, String tenant, String content, long lastModifiedTs,
            String type, String encryptedDataKey) {
        // 根据dataId, group, tenant三元组算出一个groupKey(其实就是拼接)
        String groupKey = GroupKey2.getKey(dataId, group, tenant);
        ...
        try {
        	// 根据配置文件内容算出一个md5值
            final String md5 = MD5Utils.md5Hex(content, Constants.ENCODE);
        	...
        		// 把配置文件内容dump到磁盘成为一个配置文件
                DiskUtil.saveToDisk(dataId, group, tenant, content);
            }
            // 更新缓存中的md5值
            updateMd5(groupKey, md5, lastModifiedTs, encryptedDataKey);
            return true;
        } ...
    }

ConfigCacheService的dump方法首先根据配置文件内容算出一个md5值,然后把配置文件内容dump到磁盘成为一个配置文件,最后更新缓存中的md5值。

在这里插入图片描述

    public static void updateMd5(String groupKey, String md5, long lastModifiedTs, String encryptedDataKey) {
    	// 从一个ConcurrentHashMap<String, CacheItem>中根据groupKey获取CacheItem
        CacheItem cache = makeSure(groupKey, encryptedDataKey, false);
        // 如果CacheItem等于空,或者CacheItem中的md5值与刚算出的md5值不匹配,则进入分支
        if (cache.md5 == null || !cache.md5.equals(md5)) {
        	// 更新CacheItem的md5值
            cache.md5 = md5;
            cache.lastModifiedTs = lastModifiedTs;
            // 发布一个LocalDataChangeEvent事件,异步通知客户端
            NotifyCenter.publishEvent(new LocalDataChangeEvent(groupKey));
        }
    }

ConfigCacheService的updateMd5方法首先从从一个ConcurrentHashMap<String, CacheItem>中根据groupKey获取CacheItem,然后判断如果CacheItem等于空或者CacheItem中的md5值与刚算出的md5值不匹配则进入分支,由于是刚启动,因此CacheItem肯定等于空,所以会进入if分支。然后if分支中更新CacheItem的md5值,并发布一个发布一个LocalDataChangeEvent事件异步通知客户端。

在这里插入图片描述

这里NotifyCenter.publishEvent(new LocalDataChangeEvent(groupKey))发布的事件不是Spring的事件监听机制,而是nacos自己封装的事件监听机制。

获取配置

由于已经查询MySQL然后dump到磁盘成配置文件了,因此当接收到获取配置的RPC请求时,就不需要再去查询MySQL,而是读取磁盘的配置文件即可。

我们可以根据客户端发送GRPC远程调用时创建的request对象的类型,找到服务端处理该请求的Handler。我们在上一篇文章《客户端启动源码分析》中说到request请求对象的类型是ConfigQueryRequest。

通过ConfigQueryRequest,就可以找到处理获取配置请求的方法在ConfigQueryRequestHandler的handle方法。

    public ConfigQueryResponse handle(ConfigQueryRequest request, RequestMeta meta) throws NacosException {
        
        try {
            return getContext(request, meta, request.isNotify());
        } catch (...) {...}
        
    }

ConfigQueryRequestHandler的handle方法调用getContext方法。

在这里插入图片描述

    private ConfigQueryResponse getContext(ConfigQueryRequest configQueryRequest, RequestMeta meta, boolean notify)
            throws UnsupportedEncodingException {
        ...
        						// 从磁盘根据dataId, group, tenant三元组读取配置文件
                                file = DiskUtil.targetFile(dataId, group, tenant);
                            ...
                        // 读取配置文件中的内容,设置到response对象中
                        content = readFileContent(file);
                        response.setContent(content);
                        ...
        return response;
    }

在这里插入图片描述

  • 13
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值