【个人技术经验及开发技巧分享】 【个人技术经验及开发技巧分享】
首页
  • 操作系统初识
  • JAVA基础
  • JVM
  • 开发框架
  • Redis
  • Zookeeper
  • 消息中间件
  • 持久化
  • 算法
  • 网络
  • 系统架构
  • 并发编程
  • 框架
  • 开发杂货
  • 线上排查
  • 技巧备忘
  • 部署指南
  • 版本管理
  • 工作流程
  • 发版流程
  • 友情链接
  • 网站备忘
  • 在线工具
  • 学习
  • 各种云
  • 应用下载

Louis

首页
  • 操作系统初识
  • JAVA基础
  • JVM
  • 开发框架
  • Redis
  • Zookeeper
  • 消息中间件
  • 持久化
  • 算法
  • 网络
  • 系统架构
  • 并发编程
  • 框架
  • 开发杂货
  • 线上排查
  • 技巧备忘
  • 部署指南
  • 版本管理
  • 工作流程
  • 发版流程
  • 友情链接
  • 网站备忘
  • 在线工具
  • 学习
  • 各种云
  • 应用下载
  • 并发编程

  • 框架

    • Spring
    • SpringBoot启动流程
    • Nacos注册中心
    • Nacos配置中心
      • 1 核心链路图
      • 2 阅读备忘
        • 2.1 核心流程理解
        • 2.2 刷新Environment对象详解
        • 2.3 长轮询详解
  • 源码阅读
  • 框架
luoxiaofeng
2022-06-23
目录

Nacos配置中心

# 1 核心链路图

processon链接

https://www.processon.com/view/link/62c7dfaf1e08534607e6a120 (opens new window)

nacos-config1

# 2 阅读备忘

# 2.1 核心流程理解

客户端功能描述

  1. 客户端服务启动阶段,准备 上下文 时,会根据 dataId 遍历调用Nacos服务端接口 /v1/cs/configs GET方式 获取配置文件内容并写到本地磁盘,如果本地磁盘已经存在相应配置文件,则优先使用本地配置,不调用接口。

  2. 客户端服务启动阶段,通过 spring.factories 的方式去加载Nacos相关自动配置类,这些配置类最终执行的动作是:

    • 定时调用Nacos服务端接口 /v1/cs/configs/listener POST方式 来获取有哪些配置文件发生变更,并获得相应配置文件的标识。
    • 根据这些标识再去调用Nacos服务端接口 /v1/cs/configs GET方式 获取配置文件内容。
    • 通过校验 MD5 的方式判断是否跟客户端本地的文件不一致,不一致则去刷新spring的 Environment 配置数据,并重新加载带 @RefreshScope 相关的Bean。

服务端功能描述

Nacos数据源配置为mysql

spring.datasource.platform=mysql db.num=1 db.url.0=jdbc:mysql://127.0.0.1:3306/nacos141?characterEncoding=utf8 db.user.0=root db.password.0=123456

  1. 服务启动后会去mysql查询配置信息并存储到磁盘。

    image-20220701172857093
  2. 接口 /v1/cs/configs GET方式 会去查询本地磁盘,把获取到的配置文件内容返回给客户端。

  3. 接口 /v1/cs/configs/listener POST方式 会通过长轮询的方式去比较客户端配置文件的MD5与服务端配置文件的MD5是否一致,返回不一致的数据。

  4. 管理界面发布配置会往数据库新增信息,并发布数据变更事件,通知集群其他节点同步配置。

# 2.2 刷新Environment对象详解

public synchronized Set<String> refreshEnvironment() {
// 获取刷新配置前的配置信息,对比用
 Map<String, Object> before = extract(
         this.context.getEnvironment().getPropertySources());
// 刷新Environment
 addConfigFilesToEnvironment();
// 这里上下文的Environment已经是新的值了
// 进行新旧对比,结果返回有变化的值
 Set<String> keys = changes(before,
         extract(this.context.getEnvironment().getPropertySources())).keySet();
 this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));
 return keys;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

重点在addConfigFilesToEnvironment方法,刷新Environment:

ConfigurableApplicationContext addConfigFilesToEnvironment() {
  ConfigurableApplicationContext capture = null;
  try {
    // 从上下文拿出Environment对象,copy一份
    StandardEnvironment environment = copyEnvironment(
            this.context.getEnvironment());
    // SpringBoot启动类builder,准备新做一个Spring上下文启动
    SpringApplicationBuilder builder = new SpringApplicationBuilder(Empty.class)
            // banner和web都关闭,因为只是想单纯利用新的Spring上下文构造一个新的Environment
            .bannerMode(Mode.OFF).web(WebApplicationType.NONE)
            // 传入我们刚刚copy的Environment实例
            .environment(environment);
    // 启动上下文
    capture = builder.run();
    // 这个时候,通过上下文SpringIOC的启动,刚刚Environment对象就变成带有最新配置值的Environment了
    // 获取旧的外部化配置列表
    MutablePropertySources target = this.context.getEnvironment()
            .getPropertySources();
    String targetName = null;
    // 遍历这个最新的Environment外部化配置列表
    for (PropertySource<?> source : environment.getPropertySources()) {
      String name = source.getName();
      if (target.contains(name)) {
        targetName = name;
      }
      // 某些配置源不做替换,读者自行查看源码
      // 一般的配置源都会进入if语句
      if (!this.standardSources.contains(name)) {
        if (target.contains(name)) {
          // 用新的配置替换旧的配置
          target.replace(name, source);
        } else {
          //....
        }
      }
    }
  }
}
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

新做了一个Spring上下文,因为Spring启动后会对上下文中的Environment进行初始化,获取最新配置,所以这里利用Spring的启动,达到了获取最新的Environment对象的目的。然后去替换旧的上下文中的Environment对象中的配置值。

# 2.3 长轮询详解

nacos-task
  1. 接收到客户端的请求,将请求封装成 clientLongPolling 任务提交到延时调度线程池,线程池延时时间为29.5s,同时将这个clientLongPolling任务添加到一个allSubs集合中。

  2. 在29.5秒这段期间内,没有数据变更,则29.5s后开始执行 clientLongPolling 任务,首先将 clientLongPolling 任务从 allSubs 中移除,再检查客户端的MD5和服务端的MD5值是否一致,将不一致的返回。

  3. 29.5s内服务端接收到数据更新操作,发布一个异步通知,就是将这个请求添加到一个阻塞队列,有一个线程从这个队列中取数据消费,消费逻辑是比对客户端和服务端的MD5是否一致,然后将比对结果返回,最后从 allSub 中取出相对应的 clientLongPolling 任务,将这个任务取消,这样延时队列到期将不会做任何操作。

Nacos注册中心

← Nacos注册中心

最近更新
01
SpringBoot
10-21
02
Spring
10-20
03
Sentinel
10-14
更多文章>
Copyright © 2022-2023 Louis | 粤ICP备2022060093号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式