【深入理解SpringCloud微服务】深入理解微服务配置中心原理,并手写一个微服务配置中心

news2024/9/24 11:31:23

【深入理解SpringCloud微服务】深入理解微服务配置中心原理,并手写一个微服务配置中心

  • 为什么要使用配置中心
  • 配置中心原理
  • 如何手写一个配置中心
    • 使用PropertySourceLocator
    • 监听配置变更,刷新配置
  • 实现一个微服务配置中心
    • 服务端
      • 库表
      • ConfigCenterController
      • ConfigCenterService
      • LongPollingService
      • ConfigCenterServerConfig
    • 客户端
      • ConfigService
      • SimpleMicroserviceConfigPropertySourceLocator
      • ConfigCenterClientConfig
      • LongPollingClient
      • RefreshListener
      • LongPollingClientConfig
    • 总结

为什么要使用配置中心

在没有配置中心以前,我们应用程序的配置都是保存在本地,如果是单体架构的话,这种方式是没有问题的,优点是非常的简单。

在这里插入图片描述

但是如果在微服务架构下,每个微服务自己管理自己的配置文件的话,这种方式就太混乱,不利于运维人员对配置文件的维护。

因此,在微服务架构下,通常会引入配置中心,所有微服务的配置都统一维护在配置中心,应用程序从配置中心读取配置并加载,这样对不同服务配置的维护就显得更方便。

在这里插入图片描述

配置中心原理

配置中心通常分为配置中心服务端和配置中心客户端。配置中心服务端通过某种存储方式存储配置信息,比如数据库、文件、github。而配置中心客户端请求配置中心服务端拉取配置信息,常见的是通过http请求拉取配置。

在这里插入图片描述

配置中心还会提供接口或者一个界面去进行配置添加和修改。

在这里插入图片描述
然后配置中心客户端从配置中心服务端拉取到配置信息后,会加载配置信息到本地环境中,这样我们的应用程序就能读取到从配置中心拉取回来的配置。

在这里插入图片描述

最后,配置中心通常还会有配置变更通知的功能。当配置中心发生配置变更时,需要通知客户端,可以使用与客户端维持的长连接进行推送,或者由客户端主动轮询。当客户端收到配置变更通知时,会刷新本地环境。

在这里插入图片描述

如何手写一个配置中心

以上的一整套操作,如果从零搞起,是非常麻烦的,如果我们基于Spring,使用SpringBoot开发的话,有一些接口是可以复用的,我们下面了解一下。

使用PropertySourceLocator

SpringBoot在启动的时候,会使用PropertySourceLocator#locate()方法查找配置信息,PropertySourceLocator#locate()方法返回PropertySource,里面包含了查找到的配置信息,然后SpringBoot会把PropertySource添加到Environment对象中。

在这里插入图片描述

于是,我们可以自己实现一个PropertySourceLocator重写locate()方法,locate()方法请求配置中心获取配置。我们把自己实现的PropertySourceLocator注册到Spring容器中,SpringBoot启动时就会调用我们的PropertySourceLocator去配置中心拉取配置,加载到Environment中,这样就省掉了很多代码。

在这里插入图片描述

监听配置变更,刷新配置

当配置中心发生配置变更时,要通知客户端。配置中心可以利用长连接进行推送,也可以客户端自己进行长轮询。

当客户端接收到配置变更时的环境刷新操作。客户端需要重新请求配置中心拉取配置,刷新本地环境Environment,然后还要把变更的配置重新赋值到引用该配置的对象当中。

在SpringBoot中,有一个RefreshEventListener监听器,会监听RefreshEvent事件,当RefreshEventListener监听到RefreshEvent事件时,会创建一个新SpringContext去加载配置信息,并且销毁被@RefreshScope注解修饰的bean。在下一次请求使用到被销毁的bean时,Spring会重新实例化,那么这个bean就会读取到最新的配置。

在这里插入图片描述

这样,当客户端接收到配置中心的配置变更通知时,只要通过通过Spring的事件监听机制发布一个RefreshEvent事件即可,这样又能省掉很多代码。

在这里插入图片描述

实现一个微服务配置中心

下面是我手写实现的一个配置中心,由于篇幅关系,只会展示核心代码。如果要看详细代码的话,文末会附上git仓库的地址,从上面下载即可。

服务端

配置中心服务端是基于SpringBoot开发的,采用传统的MVC架构。

库表

我们的配置中心是基于数据库存储配置信息的。

CREATE TABLE `t_config_file` (
  `f_id` bigint(20) NOT NULL,
  `f_environment` varchar(1024) DEFAULT NULL COMMENT '环境(自定义,如:dev、test)',
  `f_service_name` varchar(1024) DEFAULT NULL COMMENT '配置所属服务名',
  `f_name` varchar(1024) DEFAULT NULL COMMENT '配置文件名',
  `f_priority` int(11) DEFAULT 0 COMMENT '优先级,值越大越优先',
  `f_create_time` bigint(20) DEFAULT NULL COMMENT '创建时间',
  `f_modify_date` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT '修改时间',
  PRIMARY KEY (`f_id`),
  KEY `idx_config_file_modify_date` (`f_modify_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='配置文件表';

CREATE TABLE `t_config_file_item` (
  `f_id` bigint(20) NOT NULL,
  `f_config_file_id` bigint(20) NOT NULL COMMENT '配置文件id',
  `f_name` varchar(1024) NOT NULL COMMENT '配置项名称',
  `f_value` varchar(1024) NOT NULL COMMENT '配置项内容',
  `f_create_time` bigint(20) DEFAULT NULL COMMENT '创建时间',
  `f_modify_date` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT '修改时间',
  PRIMARY KEY (`f_id`),
  KEY `idx_config_file_item_modify_date` (`f_modify_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='配置文件配置项表';
  • t_config_file是配置文件表
  • t_config_file_item是配置文件配置项表
  • t_config_file_item通过f_config_file_id关联t_config_file表,t_config_file与t_config_file_item是一对多关系。

t_config_file里的一条记录表示一个配置文件,t_config_file_item里的一条记录表示配置文件里的一个配置项。

在这里插入图片描述

ConfigCenterController

@RestController
@RequestMapping("/config/center")
public class ConfigCenterController {

    @Autowired
    private ConfigCenterService configCenterService;

	...
	
}

ConfigCenterController暴露了配置文件的增删改查接口,调用ConfigCenterService进行处理。

在这里插入图片描述

ConfigCenterService

@Service
public class ConfigCenterService {

	...

    @Autowired
    private ConfigFileDao configFileDao;

	...

	// modify()修改配置的方法,
	// 修改完后,会调用LongPollingService通知客户端发生配置变更

	...
	
}

上面只展示了ConfigCenterService的修改配置文件和根据服务名查询所有配置文件的这两个方法。

ConfigCenterService调用ConfigFileDao操作数据库,对t_config_file表和t_config_file_item表进行增删改查操作。

其中modify方法做完修改操作后,判断如果修改操作执行成功,还会调用LongPollingService通过长连接通知客户端。

在这里插入图片描述

LongPollingService

我们采用了websocket保持客户端与服务端的长连接,当有配置变更时,通过websocket通知客户端刷新配置信息。

在这里插入图片描述

LongPollingService正是服务端暴露的websocket端点,被@ServerEndpoint注解修饰。LongPollingService里面封装了websocket连接建立和关闭时进行的操作,以及推送配置变更通知的操作。

// 对外暴露websocket端点
@ServerEndpoint("/ws/config/center")
@Component
public class LongPollingService {

	// 当建立websocket连接后,会保存websocket连接对应的Session到Map中
	// 格式为 [environmen: [serviceName: session]] 双层Map

	...
	
	// 通知客户端发生配置变更
    public static void notify(String environmen, String serviceName) throws IOException {
    	// 根据指定环境environmen指定服务名environmen,取得对应的Session
    	// 该Session代表与对应客户端建立的长连接
        Map<String, Session> serviceNameSessionMap = envServiceNameSessionMapping.get(environmen);
        if (serviceNameSessionMap != null) {
            Session session = serviceNameSessionMap.get(serviceName);
            if (session != null) {
            	// 通过session向客户端推送配置变更通知
                session.getBasicRemote().sendText(Constant.CONFIG_CHANGE_MARK);
            }
        }
    }

}

当建立websocket连接后,会保存websocket连接对应的Session到LongPollingService的Map中,格式为“[environmen: [serviceName: session]]”这样的双层Map。

LongPollingService.notify(environmen, serviceName)会通过environmen环境名(比如dev、uat)和serviceName服务名从Map中获取到对应的session,这个session表示与指定环境指定服务名的客户端建立的websocket长连接。然后通过这个session向对应客户端推送配置变更通知。

在这里插入图片描述

ConfigCenterServerConfig

ConfigCenterServerConfig是一个被@Configuration注解修饰的配置类,通过在spring.factories指定该配置类进行自动装配。

ConfigCenterServerConfig会通过@ComponentScan、@Bean等注解配置我们上面说到的类。

在这里插入图片描述

客户端

客户端也是使用SpringBoot进行开发的,以jar包的形式被其他微服务引用。

ConfigService

ConfigService是一个接口,定义了获取配置信息的方法。

public interface ConfigService {

    List<ConfigFileDto> getAll(String environment, String serviceName, String name);

    ConfigFileDto get(String fileId);

}

ConfigService的实现类内部通过OkHttp请求配置中心服务端拉取配置信息。

在这里插入图片描述

SimpleMicroserviceConfigPropertySourceLocator

SimpleMicroserviceConfigPropertySourceLocator就是我们上面说的实现了Spring的PropertySourceLocator接口的实现类,重写了locate方法,在locate方法中调用ConfigService通过http请求配置中心拉取配置信息,再把配置中心返回的配置信息转换成CompositePropertySource返回。

SimpleMicroserviceConfigPropertySourceLocator.locate(Environment)

    @Override
    public PropertySource<?> locate(Environment environment) {
    	// 创建一个CompositePropertySource,用于保存请配置中心拉取回来的配置信息
        CompositePropertySource compositePropertySource = new CompositePropertySource(PROPERTY_SOURCE_NAME);
        // 调用configService从配置中心拉取配置信息
        List<ConfigFileDto> configFileDtos = configService.getAll(simpleMicroserviceConfigProperties.getEnvironment(), simpleMicroserviceConfigProperties.getServiceName(), null);
        // 拉取回来的配置信息,保存到CompositePropertySource
        configFileDtos.stream()
                .map(configFileDto -> new MapPropertySource(configFileDto.getName(), configFileDto.getConfigMap()))
                .forEach(compositePropertySource::addFirstPropertySource);
        return compositePropertySource;
    }

SpringBoot在启动之后,会调用PropertySourceLocator的locate方法,然后把locate方法方法返回的PropertySource添加到Environment中。SpringBoot会调用这里的SimpleMicroserviceConfigPropertySourceLocator的locate方法,然后把返回的CompositePropertySource添加到Environment中。

在这里插入图片描述

ConfigCenterClientConfig

ConfigCenterClientConfig就是一个配置类,被spring.factories指定自动装配,通过@Bean注解往Springboot注册ConfigService和SimpleMicroserviceConfigPropertySourceLocator。

在这里插入图片描述

LongPollingClient

@Component
public class LongPollingClient implements ApplicationListener<ApplicationReadyEvent>, ApplicationContextAware {

    ...

    public void connect() {
        ...
        // 通过okhttp与配置中心服务端建立websocket连接
        // RefreshListener是一个监听器,收到配置中心的通知会回调
        mOkHttpClient.newWebSocket(request, new RefreshListener(...));
        ...
    }

	// 监听ApplicationReadyEvent事件,触发websocket连接建立
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        ...
        this.connect();
    }

    ...
}

LongPollingClient实现了Spring的ApplicationListener接口,监听ApplicationReadyEvent事件,触发与配置中心服务端的websocket连接建立,建立websocket连接依然是使用okhttp。

然后在创建websocket连接,设置了一个监听器RefreshListener,在收到配置中心的通知时会回调该监听器。

在这里插入图片描述

RefreshListener

RefreshListener继承了WebSocketListener,WebSocketListener是OKHttp提供的抽象类,代表一个websocket监听器。

public class RefreshListener extends WebSocketListener {

    ...

	// websocket连接建立后的回调,向配置中心服务端注册
	// 发送environmen(环境)和serviceName(服务名)到注册中心
    @Override
    public void onOpen(WebSocket webSocket, Response response) {
        JSONObject message = new JSONObject();
        message.put("environmen", environmen);
        message.put("serviceName", serviceName);
        webSocket.send(message.toJSONString());
    }

	// 收到配置中心服务端通知时回调,通过Spring事件监听机制,发一个RefreshEvent事件
    @Override
    public void onMessage(WebSocket webSocket, String text) {
        applicationContext.publishEvent(
                new RefreshEvent(this, null, "refresh config"));
    }

    ...

}

当websocket连接建立后,会回调RefreshListener的onOpen方法,会向配置中心服务端注册自己对应的environmen和serviceName,配置中心服务端会以environmen和serviceName为key,记录与该websocket连接对应的Session的映射关系,方便配置变更时通知客户端。

当客户端收到配置中心服务端的配置变更通知时,会回调RefreshListener的onMessage方法,会通过Spring事件监听机制,发一个RefreshEvent事件。发送的RefreshEvent事件会被RefreshEventListener接收,触发配置的重新加载和销毁旧的bean,上面已经说过。

在这里插入图片描述

LongPollingClientConfig

LongPollingClientConfig也是一个配置了,被spring.factories指定自动装配,通过@Bean往Spring注册一个LongPollingClient。

在这里插入图片描述

总结

这里我们可以再回顾一下服务端的LongPollingService和客户端的LongPollingClient的关系。

在这里插入图片描述

  1. 通过websocket建立了长连接
  2. 建立连接后,会回调监听器RefreshListener发送对应的environment和serviceName,LongPollingService接收到后会保存其与Session的对应关系
  3. 当服务端发生配置变更,会调用LongPollingService发送配置变更通知,LongPollingService会通过environment和serviceName取得对应的Session,通过Session推送配置变更通知
  4. 当客户端接收到配置变更通知后,会回调RefreshListener发送一个RefreshEvent事件
  5. 通过Spring的事件监听机制,RefreshEventListener接收到RefreshEvent事件,触发配置的重新加载和销毁旧的bean

由于篇幅关系,上面并没有展示详细的代码,如果想详细阅读代码的,可以git仓库下载:
https://gitee.com/huang_junyi/simple-microservice
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2093608.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

全国中学基础信息 API 数据接口

全国中学基础信息 API 数据接口 基础数据&#xff0c;高校高考&#xff0c;提供全国初级高级中学基础数据&#xff0c;定时更新&#xff0c;多维度筛选。 1. 产品功能 2024 年数据已更新&#xff1b;提供最新全国中学学校基本信息&#xff1b;包含全国初级中学与高等中学&#…

JavaWeb:实验一JSP运行环境安装及配置

一、实验目的 1&#xff0e;掌握JSP程序运行环境配置的基本要求。 2&#xff0e;熟悉HTML的常用标签使用以及静态网页的制作。 二、实验性质 验证性实验 三、实验内容 制作一个静态网站的基本页面index.html&#xff0c;要求如下&#xff1a; 1&#xff…

SQL 语言简明入门:从历史到实践

SQL&#xff08;Structured Query Language&#xff09;是数据库领域的核心语言。自20世纪70年代中期由IBM公司开发以来&#xff0c;SQL已经成为全球最广泛使用的数据库管理语言。 本文将以简洁明了的方式为您介绍SQL的历史、基本结构、核心语言组成以及其独特的特点和书写规则…

【软件部署】JumpServer堡垒机搭建及使用

目录 一、linux服务器docker搭建 二、linux服务器单机部署 1.环境安装 2.安装数据库和Redis 3.下载linux安装包并部署 4.安装启动后命令 5.通过浏览器访问部署服务器的地址 三、JumpServer产品使用 1、添加系统用户 2、创建资产 3、将主机/资源进行授权给用户 4、登录…

Day 7:条件编译

GCC编译器 预处理阶段&#xff1a; 1.gcc - E 文件名 &#xff1a;预处理文件 2.gcc - o 文件名 &#xff1a;重命名 gcc -E gcc.c gcc-o gcc.i&#xff1a;生成预处理文件。 vi gcc.i&#xff1a; 作用&#xff1a;展开头文件&#xff0c;宏进行替换。 编译阶段: gcc -…

希尔排序的图解展示与实现

什么是希尔排序 对整个数组进行预排序&#xff0c;即分组排序&#xff1a;按间距为gap分为一组&#xff0c;分组进行插入排序。 预排序的作用与特点 大的数更快地到后面&#xff0c;小的数更快地到前面&#xff1b; gap越大&#xff0c;跳得越快&#xff0c;排完接近有序慢&…

数据结构与算法---排序算法

文章目录 排序选择排序冒泡排序插入排序 希尔排序归并排序快速排序桶排序计数排序基数排序堆排序 排序 排序是指将一组数据按照特定的规则或顺序进行排列&#xff0c;比如一个数组[1, 5, 2, 4, 3]按照从小到大的顺序排列后就是[1,2,3,4,5]。 排序算法&#xff08;Sorting alg…

lay数据表格(table)的多选框限制单选

TOC lay数据表格(table)的多选框限制单选 使用layui弹窗显示表格数据提供选择&#xff0c;最初使用单选框&#xff0c;选中后无法取消勾选&#xff0c;后该成多选框限制成单选&#xff0c;可点击已勾选复选框实现取消功能。 PS&#xff1a;easyui数据表格提供简单实现 多选框…

有哪些数据分析类的软件可供参考?

对于数据分析师来说&#xff0c;掌握以下数据分析工具很有必要&#xff0c;一个好的数据分析工具&#xff0c;可以使得数据分析工作事半功倍&#xff0c;相对于整个数据分析学习流程来说&#xff0c;掌握数据分析工具是学习数据分析的关键。 日常数据分析中&#xff0c;有80%的…

【电力系统】使用电力系统稳定器 (PSS) 和静态 VAR 补偿器 (SVC) 提高瞬态稳定性

摘要 电力系统在面对故障和扰动时&#xff0c;其瞬态稳定性是确保系统安全运行的关键因素。本文探讨了通过使用电力系统稳定器&#xff08;PSS&#xff09;和静态VAR补偿器&#xff08;SVC&#xff09;来提高电力系统瞬态稳定性的策略。通过仿真分析&#xff0c;证明了PSS和SV…

折腾 Quickwit,Rust 编写的分布式搜索引擎 - 可观测性之分布式追踪

概述 分布式追踪是一种跟踪应用程序请求流经不同服务(如前端、后端、数据库等)的过程。它是一个强大的工具&#xff0c;可以帮助您了解应用程序的工作原理并调试性能问题。 Quickwit 是一个用于索引和搜索非结构化数据的云原生引擎&#xff0c;这使其非常适合用作追踪数据的后端…

提升农业信息化水平,C# ASP.NET Vue果树生长信息管理系统带来全新管理体验

&#x1f34a;作者&#xff1a;计算机毕设匠心工作室 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目…

【算法每日一练及解题思路】找出模式匹配字符串的异位词在原始字符串中出现的索引下标

【算法每日一练及解题思路】找出模式匹配字符串的异位词在原始字符串中出现的索引下标 一、题目&#xff1a;找出模式匹配字符串的异位词在原始字符串中出现的索引下标 二、举例&#xff1a; 两个字符串原始字符串initStr123sf3rtfb,模式匹配字符串regxf3s&#xff0c;找到模…

【读书笔记-《30天自制操作系统》-12】Day13

本篇的内容仍然是定时器的相关讲解。上一篇内容中对于中断程序做了许多优化&#xff0c;但是这些优化到底起了多少作用呢&#xff1f;本篇用一种测试方法来进行测试。然后本篇继续引入链表与哨兵的概念&#xff0c;进一步加快超时的中断处理。 1. 主程序简化 开发过程到了这…

nacos获取服务实例流程

一、客户端获取服务实例流程(以dubbo为例) 1.dubbo元数据服务初始化需要订阅的服务列表 1.1.获取与当前服务相同分组和集群的NACOS的注册服务列表。 1.2 首先是从spring-cloud-common的通用注册中心中&#xff0c;使用组合注册客户端类获取服务&#xff0c;此组合会逐个调用注…

Linux awk案例

目录 1. 查询时间超过2000毫秒的请求2. 查询指定列组合出现的次数3. 统计所有文件的大小4. 获取大于指定大小的文件名&#xff0c;并按照从大到小排序5. grep指定字段后&#xff0c;使用awk列转行6. 查询第四个字段等于指定值的内容 1. 查询时间超过2000毫秒的请求 ✅log: 202…

SAST :静态应用程序安全测试

目录 什么是 SAST&#xff1f; 为什么我们需要 SAST&#xff1f; SAST 解决了哪些问题&#xff1f; SAST 如何工作&#xff1f; 揭秘 SAST、DAST、IAST 和 RASP SAST 和 DAST 有什么区别&#xff1f; 典型的 SAST 优势 下一代 SAST 的增强优势 SAST的优缺点 传统 SA…

LabVIEW呼吸机测试系统开发

基于LabVIEW的呼吸机测试系统借鉴了ASL5000的设计理念&#xff0c;能够精确监控和调整呼吸机的关键性能参数&#xff0c;如氧气浓度、压力和流量等&#xff0c;以确保其在临床应用中的安全性和有效性。通过图形化编程&#xff0c;系统实现了参数的实时自动化测试&#xff0c;显…

Datawhale AI 夏令营 第五期 CV Task3

活动简介 活动链接&#xff1a;Datawhale AI 夏令营&#xff08;第五期&#xff09; 以及CV里面的本次任务说明&#xff1a;Task 3 上分思路——数据集增强与模型预测 链接里的教程非常详细&#xff0c;主要是从三个方面&#xff08;数据集增强、设置 YOLO 模型训练参数、设…

【函数模板】函数模板的重载

一、函数模板参数的重载 当函数模板的参数不同时&#xff0c;而函数名称相同时&#xff0c;会发生重载。根据调用时传入的参数不同来选择用于实例化的模板 template<typename T> void Func(T a) {std::cout << "普通单参Func函数模板调用了\n"; } temp…