如何设计一个多数据源访问工具,实现数据读写分离?基于苞米谷源码分析谈谈我对其设计的理解

一.背景

        最近我在做一个代码开发模板,需要实现对数据源的读写分离。也许我们直接在网上搜索了一下,就会发现很多读写分析的方案,其中苞米谷的方案应用最多,最成熟,它也是之前我们开发中常用的,之前老模板的搭建,我仅仅将其通用方法引入了工程模板,并未考虑其原理和实现,仅仅停留在如何使用的层面。今天我们就基于苞米谷的源码,谈谈如何设计一个多数据源访问工具。

二.方案分析

        既然我们要实现多数据源访问工具,我们在启动的时候就需要将不同的数据源加载到内存中,后续按照业务逻辑编写需要,在代码中灵活调整数据源访问。从这个思路,可以分为两大步骤,第一步加载配置创建数据源。第二大步实现数据源动态切换。接下来,我们结合苞米谷的源码,一步一步分析其实现逻辑。

三.实现步骤

第一步加载配置创建数据源

3.1:找到入口,启动加载

        启动的时候,Springboot启动器,会加载spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration

说明程序是从DynamicDataSourceAutoConfiguration开始的,我们就从它开始

3.2:掌握DynamicDataSourceAutoConfiguration做了什么事情

首先看类上注解

@Configuration
@EnableConfigurationProperties({DynamicDataSourceProperties.class})
@AutoConfigureBefore({DataSourceAutoConfiguration.class})
@Import({DruidDynamicDataSourceConfiguration.class, DynamicDataSourceCreatorAutoConfiguration.class})
@ConditionalOnProperty(prefix = "spring.datasource.dynamic",name = {"enabled"},havingValue = "true",matchIfMissing = true
)

注解的含义: 

  • DynamicDataSourceProperties 就是我们在yml或者properties可以配置的属性值,这些配置会被映射到该类对象中。
  • @Configuration 说明它是一个配置类,会在程序运行的时候自动加载
  • AutoConfigureBefore,为了防止和SpringBoot默认的启动器DataSourceAutoConfiguration冲突,设置该配置要在其自动配置之前进行配置。
  • Import,像容器中注入2个配置的BeanDefinition:
    • DruidDynamicDataSourceConfiguration:复用Druid的自动配置
    • DynamicDataSourceCreatorAutoConfiguration:该配置类,主要为了往容器注入DataSource创建器的bean。有4种创建器(默认,JNDI,Druid,Hikari)
  • ConditionalOnProperty 表明了可以通过配置spring.datasource.dynamic.enable=false来关闭动态数据源配置

其次,该类使用@ConditionalOnMissingBean的配置,向类中注入了一些bean,其中就包括从配置读取数据源配置的bean,实现对配置内容的读取,接下来我们看一下配置加载类,因为只有加载了配置,才能去创建对应的数据源

3.3:加载多数据源配置
public class YmlDynamicDataSourceProvider extends AbstractDataSourceProvider implements DynamicDataSourceProvider {private static final Logger log = LoggerFactory.getLogger(YmlDynamicDataSourceProvider.class);private Map<String, DataSourceProperty> dataSourcePropertiesMap;public Map<String, DataSource> loadDataSources() {return this.createDataSourceMap(this.dataSourcePropertiesMap);}@ConstructorProperties({"dataSourcePropertiesMap"})public YmlDynamicDataSourceProvider(Map<String, DataSourceProperty> dataSourcePropertiesMap) {this.dataSourcePropertiesMap = dataSourcePropertiesMap;}
}

该类主要是将配置加载到程序中,存放到一个Map<String, DataSourceProperty> 里面,其中的key为不同的数据源,DataSourceProperty是数据源对应的配置信息

3.4:利用上一步加载到Map<String, DataSourceProperty>的信息,创建数据源
protected Map<String, DataSource> createDataSourceMap(Map<String, DataSourceProperty> dataSourcePropertiesMap) {Map<String, DataSource> dataSourceMap = new HashMap(dataSourcePropertiesMap.size() * 2);Iterator var3 = dataSourcePropertiesMap.entrySet().iterator();while(var3.hasNext()) {Map.Entry<String, DataSourceProperty> item = (Map.Entry)var3.next();DataSourceProperty dataSourceProperty = (DataSourceProperty)item.getValue();String pollName = dataSourceProperty.getPoolName();if (pollName == null || "".equals(pollName)) {pollName = (String)item.getKey();}dataSourceProperty.setPoolName(pollName);dataSourceMap.put(pollName, this.dataSourceCreator.createDataSource(dataSourceProperty));}return dataSourceMap;}

从代码中可以看到使用迭代器方法,不断从Map中读取每一个数据源的配置信息,然后不断创建DataSource,然后将创建出来的DataSource存入一个Map<String,DataSource>的集合里面,其中的key为不同的数据源,DataSource是不同的数据源。

小结一下,整体思路我们捋一下

从DynamicDataSourceAutoConfiguration开始进行自动配置,加载数据源配置信息,然后将配置信息转换为对应的DataSource,存在一个map里面。

第二大步骤,如何实现数据源切换的,它的原理是什么?

我们使用过苞米谷的数据源切换,都知道,主要是通过@DS注解的方式来实现数据源切换,其实它的逻辑主要走AOP的切面逻辑

4.1 DynamicDataSourceAnnotationAdvisor的逻辑
 private Pointcut buildPointcut() {Pointcut cpc = new AnnotationMatchingPointcut(DS.class, true);Pointcut mpc = AnnotationMatchingPointcut.forMethodAnnotation(DS.class);return (new ComposablePointcut(cpc)).union(mpc);}

这里面主要是对@DS进行拦截,具体的处理是在DynamicDataSourceAnnotationInterceptor

4.2 DynamicDataSourceAnnotationInterceptor的逻辑
public Object invoke(MethodInvocation invocation) throws Throwable {Object var2;try {DynamicDataSourceContextHolder.push(this.determineDatasource(invocation));var2 = invocation.proceed();} finally {DynamicDataSourceContextHolder.poll();}return var2;}private String determineDatasource(MethodInvocation invocation) throws Throwable {Method method = invocation.getMethod();DS ds = method.isAnnotationPresent(DS.class) ? (DS)method.getAnnotation(DS.class) : (DS)AnnotationUtils.findAnnotation(RESOLVER.targetClass(invocation), DS.class);String key = ds.value();return !key.isEmpty() && key.startsWith("#") ? this.dsProcessor.determineDatasource(invocation, key) : key;}

这两个方法就是核心,一个是实现切换数据源,一个是决定数据源,由代码可知,决定切换到那个数据源是由key句定的,那么这个可以就是在配置文件中配置的数据源名称。

重点逻辑1.数据源是如何切换的呢?

切换数据源 DynamicDataSourceContextHolder类中

 private static final ThreadLocal<Deque<String>> LOOKUP_KEY_HOLDER = new NamedInheritableThreadLocal("dynamic-datasource") {protected Object initialValue() {return new ArrayDeque();}};

该ThreadLocal相当于为每个线程分配了一个ArrayDeque队列,虽然是队列,但是它是拿来当栈使用的。至于原因,因为ArrayDeque的效率比Stack要高。

为什么必须是栈

因为我们的调用往往是嵌套的:A->B->C 当C执行完了,数据源就应该切回B的数据源了,所以应该用栈结构实现。

小结一下

实现数据源的切换是通过AOP实现的,里面用到了数据结构ArrayDeque队列实现了栈的结构,确保数据源嵌套调用的完整性,不至于混乱。

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

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

相关文章

LocalDateTime,OffsetDateTime和ZonedDateTime(上)

图片来源&#xff1a;https://www.cnblogs.com/yourbatman/p/14324575.html 一. LocalDate和LocalTime LocalDate&#xff1a;代表不含时区信息的日期&#xff0c;它只能表示年、月、日。它适用于记录一个日子&#xff0c;比如生日、纪念日、或者任何只需要日期而不需要具体时…

前端框架大比拼:React、Angular、Vue、Svelte、Ember,哪个才是你的终极选择!

前端开发框架在现代Web开发中扮演着至关重要的角色。它们不仅提高了开发效率&#xff0c;还改善了代码的可维护性和扩展性。以下是一些流行的前端框架及其对比&#xff0c;帮助你选择最适合你项目需求的框架。 1. React 优点: 生态系统丰富&#xff1a; React 拥有庞大的社区…

【SpringCloud】注册中心的其他实现 - Nacos

目录 注册中心的其他实现-NacosNacos简介Nacos安装下载安装包Windows解压修改单机模式启动Nacos常见问题 Linux准备安装包单机模式启动常见问题 Nacos快速上手服务注册/服务发现引入Spring Cloud Alibaba依赖引入Nacos 依赖引入Load Balance依赖 配置Nacos地址远程调用启动服务…

SpringCloud微服务实现服务熔断的实践指南

Spring Cloud是一套分布式系统的微服务框架&#xff0c;它提供了一系列的组件和工具&#xff0c;能够使我们更容易地构建和管理微服务架构。在实际开发中&#xff0c;由于各个服务之间的通信依赖&#xff0c;一旦某个服务出现故障或负载过高&#xff0c;可能会导致整个系统的性…

百度飞浆Paddle OCR检测和识别【OCR数据收集、标注、数据集划分、检测识别模型训练、导出模型】

文章目录 前言一、OCR数据集采集二、OCR数据标注三、划分数据集四、数据训练五、导出模型 前言 1、我的电脑没有GPU&#xff0c;如果不使用AI Studio训练的话&#xff0c;第一遍我是按照CPU进行环境配置和训练的&#xff0c;可以参考这篇文章&#xff0c;我按着弄了一遍&#…

深度学习(一)——CMC特刊推荐

点击蓝字 关注我们 特刊征稿 01 期刊名称&#xff1a; Multimedia Security in Deep Learning 截止时间&#xff1a; 提交截止日期:2024年9月30日 目标及范围&#xff1a; 题为“深度学习中的多媒体安全”的特刊是一个平台&#xff0c;旨在推动深度学习在多媒体安全领域的创…

山体滑坡检测系统源码分享

山体滑坡检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

(黑马点评)八、实现签到统计和uv统计

8.1 签到统计系列功能 8.1.1 认识BitMap结构 BitMap是Redis基于String实现的一种高效的二进制数位的数据结构。因此一个BItMap的最大上线为512M&#xff0c;转为bit位可表示 2^32位 常见命令 SETBIT&#xff1a;向指定位置&#xff08;offset&#xff09;存入一个0或1 GETBIT …

CST软件超表面---电容或变容二极管调焦反射镜

可变焦的超表面&#xff08;focus tunable metasurface&#xff09;类型反射镜具有超薄、智能可控等特点&#xff0c;可用于成像、显微等应用。而且经常有朋友问如何用电容或二极管调控超材料&#xff0c;这期我们就用个简单的案例看看调控效果。 1. 单元仿真 单元尺寸我们用1…

【原创教程】电气电工20:一文弄透电气电工辅材

电气电工这些知识点,我们描述的比较细,虽然看起来比较简单,但是它是后面我们技能提升的基础,如果我们后面学电气工程师相关知识,这些都属于基本功。 接着我们来看一下电气辅材。 电气辅材定义: 电气辅材是指与电气设备固有的元器件配套使用的配件和器具。常见的电气辅…

Python “函数” ——Python面试100道实战题目练习,巩固知识、检查技术、成功就业

本文主要是作为Python中函数的一些题目&#xff0c;方便学习完Python的函数之后进行一些知识检验&#xff0c;感兴趣的小伙伴可以试一试&#xff0c;含选择题、判断题、实战题、填空题&#xff0c;答案在第五章。 在做题之前可以先学习或者温习一下Python的函数&#xff0c;推荐…

构建高效用户中心的技术方案

一、架构设计 在设计用户中心时&#xff0c;首先要考虑其架构。推荐采用微服务架构&#xff0c;这样可以将不同功能模块独立开来&#xff0c;便于后期维护和扩展。例如&#xff0c;可以将用户注册、登录、信息管理、权限控制等功能分为不同的服务模块。 二、前端开发 前端是…

【2022工业图像异常检测文献】PatchCore

Towards Total Recall in Industrial Anomaly Detection 1、Background 工业图像异常检测主要解决 冷启动问题 &#xff0c;即仅使用正常&#xff08;无缺陷&#xff09;样本图像来训练模型。 现有的关于冷启动工业视觉异常检测的工作依赖于通过自编码方法、生成对抗网络或其…

高速CT滑环的特点分析

高速CT滑环在现代成像技术中发挥着至关重要的作用&#xff0c;尤其是在医学成像设备和工业检测系统中。这种滑环不仅满足高速旋转的需求&#xff0c;还确保了信号和电力的稳定传输。本文将详细分析高速CT滑环的主要特点及其应用优势。 高速CT滑环的第一个特点是其高传输速率。…

101. 对称二叉树【同时遍历两棵树】【C++】

题目描述 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true 示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#xff1a;false 提示&#xff1a; …

Flet内置图标库ft.icons的图标如何使用庖丁解牛深度解读讲解,源代码IDE运行和调试通过

序言 Flet内置图标库图标丰富多彩、包罗万象。flet内置图标库指ft.icons图标库。使用Flet框架编写程序和项目&#xff0c;就要使用 Flet 内置图标库图标。使用 Flet 内置图标库的图标好处很多。 具体说有以下好处&#xff1a; 便捷性&#xff1a;无需额外寻找和下载图标资源&…

「OC」present和push操作区别以及混合推出的实现

「OC」present和push操作区别以及混合推出的实现 文章目录 「OC」present和push操作区别以及混合推出的实现前言present用途while循环越级返回通知越级返回添加present动画 push模态视图和push视图混合跳转操作一&#xff1a;控制器Apresent控制器B&#xff0c;控制器B再将控制…

影视会员充值api?接口对接需要做哪些准备工作?

影视会员充值 API 接口对接主要有以下步骤&#xff1a; 1.前期准备 明确自身需求&#xff1a;确定你希望通过 API 接口实现的功能&#xff0c;例如支持哪些影视平台的会员充值、是否需要获取会员信息、是否需要订单查询功能等。选择合适的 API 提供商&#xff1a;官方视频平台…

中国电子学会202312青少年软件编程(Python)等级考试试卷(三级)真题

2023年12月青少年软件编程Python等级考试(三级)真题试卷 题目总数:38 总分数:100 一、选择题 第 1 题 单选题 一个非零的二进制正整数,在其末尾添加两个“0”,则该新数将是原数的?( ) A.10倍 B.2倍 C.4倍 D.8倍 第 2 题 单选题 2023年亚运会将在杭…

分步指南:如何使用 ChatGPT 撰写文献综述

撰写文献综述对于研究人员和学生来说,往往是一项既耗时又复杂的任务。这一过程不仅要求对所选主题的现有研究进行全面的了解和掌握,还需要学术严谨性。然而,随着像 ChatGPT 这样的高级语言模型的广泛应用,撰写文献综述的过程变得更加高效和简化。通过合理利用 ChatGPT,研究…