Java essay Java essay
首页
  • Java基础
  • Java进阶
  • 设计模式
  • Java你不知道的小事
  • Spring初识
  • Spring进阶
  • SpringBoot基础
  • SpringBoot进阶
  • 什么是微服务
  • SpringCloud全家桶
  • Dubbo
  • SpringCloud Alibaba
  • Zookeeper
  • Nginx
  • RabbitMQ
  • RocketMQ
  • Docker入门到精通
  • 性能分析工具
  • 数据库性能优化
  • 性能优化
  • Java基础面试必问
  • JavaWeb面试必问
  • Java框架面试必问
  • 数据库面试必问
  • 中间件面试必问
  • 分布式微服务面试必问
  • Linux面试必问
  • 计算机网络面试必问
  • 开放性问题面试问必问
  • 简介
  • 联系我

Mr.Fire

后端程序员一枚
首页
  • Java基础
  • Java进阶
  • 设计模式
  • Java你不知道的小事
  • Spring初识
  • Spring进阶
  • SpringBoot基础
  • SpringBoot进阶
  • 什么是微服务
  • SpringCloud全家桶
  • Dubbo
  • SpringCloud Alibaba
  • Zookeeper
  • Nginx
  • RabbitMQ
  • RocketMQ
  • Docker入门到精通
  • 性能分析工具
  • 数据库性能优化
  • 性能优化
  • Java基础面试必问
  • JavaWeb面试必问
  • Java框架面试必问
  • 数据库面试必问
  • 中间件面试必问
  • 分布式微服务面试必问
  • Linux面试必问
  • 计算机网络面试必问
  • 开放性问题面试问必问
  • 简介
  • 联系我
  • 微服务概述

  • SpringCloud全家桶

  • SpringCloudAlibaba

    • Alibaba Nacos快速入门(一)
    • Alibaba Nacos快速入门(二)
    • Alibaba Nacos快速入门(三)
    • Alibaba Nacos快速入门(四)
    • Alibaba Nacos配置中心动态感知原理
      • 前言
      • 动态监听之Pull和Push
      • Nacos的Pull模式
      • Nacos的动态感知
        • LongPollingService 监听事件类
        • DataChangeTask线程
      • 总结
  • 分布式锁

  • 分布式微服务
  • SpringCloudAlibaba
Mr.Fire
2023-02-25
目录

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继承AbstractEventListener,AbstractEventListener是事件抽象类,它有一个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));
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11

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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  1. 遍历allSubs中的客户端长轮询请求。
  2. 比较每个客户端长轮询请求携带的groupKey,如果服务端变更的配置和客户端请求关注的配置一致,则直接返回,这里调用clientSub.sendResponse()方法返回。

# 总结

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

  1. 客户端通过长轮询的方式发起Pull请求服务端获取配置变更;
  2. 服务端判断如果是长轮询请求,对比数据的MD5,如果发生变化则直接返回,否则通过延迟任务执行ClientLongPolling线程;
  3. 配置中心修改配置后,会发布ConfigDataChangeEvent事件;
  4. EventDispatcher触发事件,通知监听者。LongPollingService就是监听者之一。
  5. 监听者通过线程池开启定时线程,遍历客户端的所有长轮询的请求, 通过groupKey匹配到对应请求,直接返回。
最后更新时间: 2023/03/01, 21:53:39
Alibaba Nacos快速入门(四)
分布式锁的几种实现方式

← Alibaba Nacos快速入门(四) 分布式锁的几种实现方式→

最近更新
01
分布式系统核心理论CAP和BASE理论
03-05
02
分布式锁的几种实现方式
03-05
03
一文详解ThreadLocal是什么
03-01
更多文章>
Theme by Vdoing | Copyright © 2021-2023 Mr.Fire | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式