Alibaba Nacos配置中心动态感知原理示例解析

 更新时间:2023年08月02日 08:44:25   作者:Mr.Fire  
这篇文章主要介绍了Alibaba Nacos配置中心动态感知原理示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

Nacos提供两大核心功能,服务注册发现,配置中心。对应Nacos的架构图,分别是Naming Service和Config Service,其中Config Service是实现配置中心的核心模块。实现了版本管理、灰度发布、监听管理、推送轨迹等功能。针对配置中心,当我们通过控制台或API修改配置之后,客户端能动态获取到修改后的配置,那么配置中心是如何实现动态感知的呢?

动态监听之Pull和Push

当Nacos Config Server上的配置发生变化时,需要让相关的应用程序感知到配置的变化,这就需要客户端对感兴趣的配置实现监听。那么客户端是如何实现配置变更实时更新的呢?

一般来说,客户端与服务端的交互无非两种:Pull模式和Push模式,一个是客户端主动拉取,一个是服务端主动推送。

  • Pull模式:服务端和客户端之间需要维护长连接,客户端多的情况下耗内存、需要心跳机制检测连接状态。
  • Push模式:客户端需要定时拉取数据,不能保证实时性,服务端长时间不更新的情况下,定时任务为无效更新,浪费资源。

Nacos的Pull模式

Nacos采用的是Pull模式,不过不是简单的Pull,而是一种长轮询机制。结合Pull和Push两者的优势,客户端采用长轮询的方式发起Pull请求,检查服务配置消息是否发生变化,如果更新,客户端会根据变更的内容获取最新配置信息。

所谓的长轮询,就是客户端发起Pull请求之后,服务端如果发生配置变更则立即返回,如果服务端和客户端配置是保持一致的,那么服务端会“Hold”住这个请求,在指定时间内不返回结果,直到这段时间内配置发生变化。这个长连接默认超时时间是30s。

服务端收到请求后,先检查配置是否发生变化,如果没变化,则设置一个定时任务,延期29.5s执行,并且把当前的客户端长轮询连接加入allSubs队列。这里有两种方式触发连接返回。

  • 等待29.5s自动触发检查机制,无论是否发生变化,都会返回。
  • 在29.5s内,通过Nacos控制台或者API的形式对配置进行了修改,会触发ConfigDataChangeEvent事件。

Nacos的动态感知

前面我们已经知道客户端通过长轮询请求来获取配置变更,但是定时任务是延迟执行的,那并没有达到实时的目的,当通过控制台或者API修改配置时,那Nacos是如何实时动态更新的呢?

LongPollingService 监听事件类

LongPollingService继承AbstractEventListenerAbstractEventListener是事件抽象类,它有一个onEvent抽象方法,而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));
        }
    }
}

LongPollingService可以看到LocalDataChangeEvent事件,这个事件是服务端的配置数据发生变化时发布的一个事件。onEvent方法中通过线程池来执行一个DataChangeTask任务。

DataChangeTask线程

DataChangeTask是一个线程,实现了Runnable接口,对应的run()如下:

class DataChangeTask implements Runnable {
    @Override
    public void run() {
        try {
            ConfigService.getContentBetaMd5(groupKey);
            for (Iterator<ClientLongPolling> iter = allSubs.iterator(); iter.hasNext(); ) {
                ClientLongPolling clientSub = iter.next();
                if (clientSub.clientMd5Map.containsKey(groupKey)) {
                    // 如果beta发布且不在beta列表直接跳过
                    if (isBeta && !betaIps.contains(clientSub.ip)) {
                        continue;
                    }
                    // 如果tag发布且不在tag列表直接跳过
                    if (StringUtils.isNotBlank(tag) && !tag.equals(clientSub.tag)) {
                        continue;
                    }
                    getRetainIps().put(clientSub.ip, System.currentTimeMillis());
                    iter.remove(); // 删除订阅关系
                    LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}|{}",
                        (System.currentTimeMillis() - changeTime),
                        "in-advance",
                        RequestUtil.getRemoteIp((HttpServletRequest)clientSub.asyncContext.getRequest()),
                        "polling",
                        clientSub.clientMd5Map.size(), clientSub.probeRequestSize, groupKey);
                    clientSub.sendResponse(Arrays.asList(groupKey));
                }
            }
        } catch (Throwable t) {
            LogUtil.defaultLog.error("data change error:" + t.getMessage(), t.getCause());
        }
    }
    DataChangeTask(String groupKey) {
        this(groupKey, false, null);
    }
    DataChangeTask(String groupKey, boolean isBeta, List<String> betaIps) {
        this(groupKey, isBeta, betaIps, null);
    }
    DataChangeTask(String groupKey, boolean isBeta, List<String> betaIps, String tag) {
        this.groupKey = groupKey;
        this.isBeta = isBeta;
        this.betaIps = betaIps;
        this.tag = tag;
    }
    final String groupKey;
    final long changeTime = System.currentTimeMillis();
    final boolean isBeta;
    final List<String> betaIps;
    final String tag;
}
  • 遍历allSubs中的客户端长轮询请求。
  • 比较每个客户端长轮询请求携带的groupKey,如果服务端变更的配置和客户端请求关注的配置一致,则直接返回,这里调用clientSub.sendResponse()方法返回。

总结

好了,最后整理下nacos实时动态感知的流程如下:

  • 客户端通过长轮询的方式发起Pull请求服务端获取配置变更;
  • 服务端判断如果是长轮询请求,对比数据的MD5,如果发生变化则直接返回,否则通过延迟任务执行ClientLongPolling线程;
  • 配置中心修改配置后,会发布ConfigDataChangeEvent事件;
  • EventDispatcher触发事件,通知监听者。LongPollingService就是监听者之一。
  • 监听者通过线程池开启定时线程,遍历客户端的所有长轮询的请求, 通过groupKey匹配到对应请求,直接返回。

以上就是Alibaba Nacos配置中心动态感知原理示例解析的详细内容,更多关于Alibaba Nacos配置中心动态感知的资料请关注脚本之家其它相关文章!

相关文章

  • Java中Lambda表达式并行与组合行为

    Java中Lambda表达式并行与组合行为

    这篇文章主要介绍了Java中Lambda表达式并行与组合行为,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2017-02-02
  • JAVA中DIFF算法实现

    JAVA中DIFF算法实现

    本文主要介绍了JAVA中DIFF算法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • java 实现获取指定位置后的第一个数字

    java 实现获取指定位置后的第一个数字

    这篇文章主要介绍了java 实现获取指定位置后的第一个数字,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • SpringMvc静态资源访问实现方法代码实例

    SpringMvc静态资源访问实现方法代码实例

    这篇文章主要介绍了SpringMvc静态资源访问实现方法代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • Spring实现处理跨域请求代码详解

    Spring实现处理跨域请求代码详解

    这篇文章主要介绍了Spring实现处理跨域请求代码详解,具有一定借鉴价值,需要的朋友可以了解下。
    2017-12-12
  • SpringBoot前后端分离实现验证码操作

    SpringBoot前后端分离实现验证码操作

    验证码的功能是防止非法用户恶意去访问登录接口而设置的一个功能,今天我们就来看看在前后端分离的项目中,SpringBoot是如何提供服务的
    2022-05-05
  • Java多线程中断机制三种方法及示例

    Java多线程中断机制三种方法及示例

    这篇文章主要介绍了Java多线程中断机制三种方法及示例,向大家分享了这三种方法的介绍几代码示例,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • 关于如何正确地定义Java内部类方法详解

    关于如何正确地定义Java内部类方法详解

    在Java中,我们通常是把不同的类创建在不同的包里面,对于同一个包里的类来说,它们都是同一层次的,但其实还有另一种情况,有些类可以被定义在另一个类的内部,本文将详细带你了解如何正确地定义Java内部类,需要的朋友可以参考下
    2023-05-05
  • Java WebService 简单实例(附实例代码)

    Java WebService 简单实例(附实例代码)

    本篇文章主要介绍了Java WebService 简单实例(附实例代码), Web Service 是一种新的web应用程序分支,他们是自包含、自描述、模块化的应用,可以发布、定位、通过web调用。有兴趣的可以了解一下
    2017-01-01
  • Spring Boot Swagger2使用方法过程解析

    Spring Boot Swagger2使用方法过程解析

    这篇文章主要介绍了Spring Boot Swagger2使用方法过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08

最新评论