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

【深入理解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 {@Autowiredprivate ConfigCenterService configCenterService;...}

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

在这里插入图片描述

ConfigCenterService

@Service
public class ConfigCenterService {...@Autowiredprivate 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)

    @Overridepublic PropertySource<?> locate(Environment environment) {// 创建一个CompositePropertySource,用于保存请配置中心拉取回来的配置信息CompositePropertySource compositePropertySource = new CompositePropertySource(PROPERTY_SOURCE_NAME);// 调用configService从配置中心拉取配置信息List<ConfigFileDto> configFileDtos = configService.getAll(simpleMicroserviceConfigProperties.getEnvironment(), simpleMicroserviceConfigProperties.getServiceName(), null);// 拉取回来的配置信息,保存到CompositePropertySourceconfigFileDtos.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连接建立@Overridepublic 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(服务名)到注册中心@Overridepublic void onOpen(WebSocket webSocket, Response response) {JSONObject message = new JSONObject();message.put("environmen", environmen);message.put("serviceName", serviceName);webSocket.send(message.toJSONString());}// 收到配置中心服务端通知时回调,通过Spring事件监听机制,发一个RefreshEvent事件@Overridepublic 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.rhkb.cn/news/413607.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis从入门再再到入门(下)

文章目录 1.Redis远程连接1.1 Redis远程连接配置1.2 通过桌面版图形化界面连接Redis1.3 通过IDEA中的插件连接Redis 2.Jedis的基本使用2.1 jedis概述2.2 jedis的基本操作2.3 jedis连接池 3.Spring整合Redis3.1 新建maven工程,引入相关依赖3.2 redis.properties3.3 spring-redis…

Python基础性知识(中部分)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言1、Python中的语句1.1 顺序语句1.2 条件语句1.3 循环语句1.3.1 while循环1.3.2 for循环1.3.3 break与continue语句 1.4 综合三大语句制作小游戏--人生重开模拟器…

算法设计与分析:实验五 图论——桥问题

实验内容&#xff1a; 1. 桥的定义 在图论中&#xff0c;一条边被称为“桥”代表这条边一旦被删除&#xff0c;这张图的连通块数量会增加。等价地说&#xff0c;一条边是一座桥当且仅当这条边不在任何环上。一张图可以有零或多座桥。 2. 求解问题 找出一个无向图中所有的桥…

若依,前后端分离项目,部署到服务器

1.后端项目用maven打包 正式服的话&#xff0c;测试不用加。 application.yml加上context-path: /prod-api 一定要选择root的ruoyi&#xff0c;他会把你自动打包其他模块的依赖 全部成功。然后去ruoyi-admin拿到这个包&#xff0c;java -jar ruoyi-admin.jar就可以了 将jar上…

Linux上启动redis

1.默认启动方式:在系统的任意位置执行 redis-server即可启动 ps:这是前端界面启动&#xff0c;无法直接连接redis&#xff0c;想要连接的话只能另外启动一个窗口&#xff0c;因此下面我们介绍后台启动redis 2.指定配置启动&#xff1a; redis的配置文件位置&#xff1a…

数学建模--皮尔逊相关系数、斯皮尔曼相关系数

目录 1.总体的皮尔逊相关系数 2.样本的皮尔逊相关系数 3.对于皮尔逊相关系数的认识 4.描述性统计以及corr函数 ​编辑 5.数据导入实际操作 6.引入假设性检验 6.1简单认识 6.2具体步骤 7.p值判断法 8.检验正态分布 8.1jb检验 8.2威尔克检验&#xff1a;针对于p值进行…

Java基础(6)- Java代码笔记3

目录 一、二维数组 1.二维数组定义 a.动态初始化 b.静态初始化 c.简单静态初始化 2.获取数组长度 二、方法 1.无参无返回值方法 2.有参无返回值方法 3.无参有返回值方法 4.有参有返回值方法 5.形式参数和实际参数 6.三层架构思想 7.方法注意事项 8.数组作为方法…

深度强化学习算法(六)(附带MATLAB程序)

深度强化学习&#xff08;Deep Reinforcement Learning, DRL&#xff09;结合了深度学习和强化学习的优点&#xff0c;能够处理具有高维状态和动作空间的复杂任务。它的核心思想是利用深度神经网络来逼近强化学习中的策略函数和价值函数&#xff0c;从而提高学习能力和决策效率…

8.30工作笔记

要做的事情&#xff1a; 1 测试剩下的三个因子&#xff1a;coppock 潮汐因子 云开雾散 2 整理需要时间序列的因子 以及截面因子 3 灾后重建多了一列&#xff0c;灾后重建’所有值都是nan&#xff0c;这里不仅是灾后重建&#xff0c;所有的都要改 4 coppock 潮汐因子 云开雾散在…

【Qt】菜单栏

目录 菜单栏 例子&#xff1a;创建菜单栏、菜单、菜单项 例子&#xff1a;给菜单设置快捷键 例子&#xff1a;给菜单项设置快捷键 例子&#xff1a;添加子菜单 例子&#xff1a;添加分隔线 例子&#xff1a;添加图标 菜单栏 Qt中的菜单栏是通过QMenuBar这个类实现的&…

MySQL:复合查询

MySQL&#xff1a;复合查询 聚合统计分组聚合统计group byhaving 多表查询自连接子查询单行子查询多行子查询多列子查询from子查询 合并查询unionunion all 内连接外连接左外连接右外连接全外连接 视图 MySQL 复合查询是数据分析和统计的强大工具&#xff0c;本博客将介绍如何使…

当AI遇上制药:加速跑向未来的快车道,还是布满荆棘的征途?

01 在全球科技领域&#xff0c;AI的崛起无疑掀起了一场变革的风暴&#xff0c;其影响力已渗透至各行各业&#xff0c;促使各领域积极寻求与AI技术的深度融合&#xff0c;以提升效率、创新产品及优化服务。在医疗健康领域&#xff0c;AI与制药的结合自2007年起航&#xff0c;历…

第八周:机器学习

目录 摘要 Abstract 一、注意力机制V.S.自注意力机制 1、引入 2、注意力机制 3、自注意力机制 二、自注意力机制 1、输入 2、输出 3、序列标注 4、Multi-head Self-attention 5、比较 总结 摘要 前两周学习了CNN的基本架构&#xff0c;针对全局信息的考虑问题&…

行为识别实战第二天——Yolov5+SlowFast+deepsort: Action Detection(PytorchVideo)

Yolov5SlowFastdeepsort 一、简介 YoloV5SlowFastDeepSort 是一个结合了目标检测、动作识别和目标跟踪技术的视频处理框架。这一集成系统利用了各自领域中的先进技术&#xff0c;为视频监控、体育分析、人机交互等应用提供了一种强大的解决方案。 1. 组件说明&#xff1a; Y…

如何通过住宅代理进行高效SSL检查

引言 什么是SSL检查&#xff1f;有哪些内容&#xff1f; 为什么要使用SSL检查&#xff1f; SSL检查是如何进行的&#xff1f; 总结 引言 在现代互联网环境中&#xff0c;SSL/TLS协议已成为确保网络通信安全的基石。随着网络攻击手段的不断演进&#xff0c;仅仅依赖于基础的…

数据中心和算力中心的区别

数据中心&#xff08;Data Center&#xff09;和算力中心&#xff08;Computing Power Center 或 HPC Center&#xff09;虽然都涉及数据处理和存储&#xff0c;但它们的重点和用途有所不同。下面将详细介绍两者之间的区别&#xff1a; 数据中心&#xff08;Data Center&#x…

torch、torchvision、torchtext版本兼容问题

1、torch与torchtext版本兼容 参考torchtext PyPI 2、 torch与torchvision版本兼容 参考torchvision PyPI

【最新华为OD机试E卷】最长连续方波信号(200分)-多语言题解-(Python/C/JavaScript/Java/Cpp)

🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-E/D卷的三语言AC题解 💻 ACM金牌🏅️团队| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,…

从跟跑到领跑:AIGC时代国产游戏的崛起与展望

引言 在人工智能技术快速发展的背景下,AIGC(人工智能生成内容)时代的到来正在重新定义游戏产业的未来。人工智能技术,尤其是生成对抗网络(GAN)、自然语言处理(NLP)、深度学习等领域的突破,正在为游戏开发带来前所未有的机会和挑战。这些技术不仅改变了游戏内容的创作…

51单片机-定时器介绍

时间&#xff1a;2024.8.31 作者&#xff1a;Whappy 目的&#xff1a;手撕51 代码&#xff1a; 现象&#xff1a;