当前位置: 首页 > news >正文

【源码】Sharding-JDBC源码分析之Yaml分片配置文件解析原理

 Sharding-JDBC系列

1、Sharding-JDBC分库分表的基本使用

2、Sharding-JDBC分库分表之SpringBoot分片策略

3、Sharding-JDBC分库分表之SpringBoot主从配置

4、SpringBoot集成Sharding-JDBC-5.3.0分库分表

5、SpringBoot集成Sharding-JDBC-5.3.0实现按月动态建表分表

6、【源码】Sharding-JDBC源码分析之JDBC

7、【源码】Sharding-JDBC源码分析之SPI机制

8、【源码】Sharding-JDBC源码分析之Yaml分片配置文件解析原理

前言

使用Sharding JDBC开发时,最核心的操作为分片规则配置,本篇从源码的角度分享Sharding JDBC分片规则配置的解析过程。

ShardingSphereDriver

在【源码】Sharding-JDBC源码分析之JDBC-CSDN博客这篇博文中,介绍了Sharding-JDBC提供了原生JDBC驱动ShardingSphereDriver。

package org.apache.shardingsphere.driver;import org.apache.shardingsphere.driver.jdbc.core.driver.DriverDataSourceCache;import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.util.Properties;
import java.util.logging.Logger;public final class ShardingSphereDriver implements Driver {private static final int MAJOR_DRIVER_VERSION = 5;private static final int MINOR_DRIVER_VERSION = 1;// 数据库连接缓存private final DriverDataSourceCache dataSourceCache = new DriverDataSourceCache();static {try {// 注册到DriverManagerDriverManager.registerDriver(new ShardingSphereDriver());} catch (final SQLException ex) {throw new DriverRegisterException(ex);}}/*** 在HikariDataSource等连接池中,会通过DriverManager.getDriver(url)获得该ShardingSphereDriver,* 调用ShardingSphereDriver.connect()获得数据库连接。*/@Overridepublic Connection connect(final String url, final Properties info) throws SQLException {return acceptsURL(url) ? dataSourceCache.get(url).getConnection() : null;}/*** ShardingSphereDriver的url中必须是以jdbc:shardingsphere:开头*/@Overridepublic boolean acceptsURL(final String url) {return null != url && url.startsWith("jdbc:shardingsphere:");}@Overridepublic DriverPropertyInfo[] getPropertyInfo(final String url, final Properties info) {return new DriverPropertyInfo[0];}@Overridepublic int getMajorVersion() {return MAJOR_DRIVER_VERSION;}@Overridepublic int getMinorVersion() {return MINOR_DRIVER_VERSION;}/*** 不符合JDBC标准* @return*/@Overridepublic boolean jdbcCompliant() {return false;}@Overridepublic Logger getParentLogger() {return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);}
}

ShardingSphereDriver类实现了原生JDBC的Driver接口,该接口提供了connect()接口,返回一个Connection数据库连接。

在HikariDataSource等连接池中,会通过DriverManager.getDriver(url)获得该ShardingSphereDriver,调用ShardingSphereDriver.connect()获得数据库连接。

在connect()方法中,通过url从DriverDataSourceCache中获取一个DataSource,然后执行DataSource.getConnection()获取一个Connection连接。

DriverDataSourceCache

DriverDataSourceCache的源码如下:

package org.apache.shardingsphere.driver.jdbc.core.driver;/*** Driver中的数据源缓存。通过解析yaml中配置的数据源,创建ShardingSphereDataSource,并缓存*/
public final class DriverDataSourceCache {private final Map<String, DataSource> dataSourceMap = new ConcurrentHashMap<>();/*** 获取一个DataSource。从Map中获取,如果不存在,则调用createDataSource创建一个DataSource*/public DataSource get(final String url) {if (dataSourceMap.containsKey(url)) {return dataSourceMap.get(url);}return dataSourceMap.computeIfAbsent(url, DriverDataSourceCache::createDataSource);}/*** 解析yaml分片配置文件,并创建一个ShardingSphereDataSource*/@SuppressWarnings("unchecked")private static <T extends Throwable> DataSource createDataSource(final String url) throws T {try {// 1、通过ShardingSphereDriverURL,读取sharding分片配置yaml文件信息// 2、通过YamlShardingSphereDataSourceFactory,解析分片配置yaml文件,创建一个ShardingSphereDataSourcereturn YamlShardingSphereDataSourceFactory.createDataSource(new ShardingSphereDriverURL(url).toConfigurationBytes());} catch (final IOException ex) {throw (T) new SQLException(ex);} catch (final SQLException ex) {throw (T) ex;}}
}

1)get()方法,以url为key,从Map中获取一个DataSource,如果不存在,则调用createDataSource()创建一个DataSource;

2)createDataSource()方法,解析yaml中配置的分片规则,创建一个ShardingSphereDataSource对象,并返回;

a)通过ShardingSphereDriverURL,读取sharding分片配置yaml文件信息。

构造方法中,截取url中的jdbc:shardingsphere:字符串,获取分片配置的文件名。在toConfigurationBytes()方法中,读取配置文件,过滤掉#开头的行,转成byte[]数组返回;

b)通过YamlShardingSphereDataSourceFactory.createDateSource(),解析分片配置的yaml文件的byte[]数组,创建一个ShardingSphereDataSource;

在YamlShardingSphereDataSourceFactory.createDateSource()方法中,通过YamlEngine.unmarshal()方法,获得一个YamlRootConfiguration对象。该对象为配置的分片规则信息对象。

YamlEngine.unmarshal()方法的代码如下:

    /*** 将yaml格式的配置信息转化成对应classType的对象*/public static <T extends YamlConfiguration> T unmarshal(final byte[] yamlBytes, final Class<T> classType) throws IOException {try (InputStream inputStream = new ByteArrayInputStream(yamlBytes)) {return new Yaml(new ShardingSphereYamlConstructor(classType)).loadAs(inputStream, classType);}}

其中传入的classType为YamlRootConfiguration.class。

该方法先创建一个ShardingSphereYamlConstructor对象,然后创建Yaml对象,通过Yaml对象的loadAs()将文件信息封装成一个YamlRootConfiguration对象。

Yaml对象为snakeyaml包的类,该工具类用于解析yaml文件。

ShardingSphereYamlConstructor

ShardingSphereYamlConstructor的源码如下:

/*** Sharding Sphere Yaml构造器。该类继承snake yaml包中的Constructor类,*/
public class ShardingSphereYamlConstructor extends Constructor {private final Map<Class<?>, Construct> typeConstructs = new HashMap<>();private final Class<?> rootClass;public ShardingSphereYamlConstructor(final Class<?> rootClass) {super(rootClass, new LoaderOptions() {{setCodePointLimit(Integer.MAX_VALUE);}});// 通过Java SPI获取ShardingSphereYamlConstruct,并添加到typeConstructs中ShardingSphereServiceLoader.getServiceInstances(ShardingSphereYamlConstruct.class).forEach(each -> typeConstructs.put(each.getType(), each));Map<String, Class<?>> yamlShortcuts = new HashMap<>();// 通过Java SPI获取ShardingSphereYamlShortcuts,执行ShardingSphereYamlShortcuts.getYamlShortcuts()方法。// 将结果添加到yamlShortcuts中ShardingSphereServiceLoader.getServiceInstances(ShardingSphereYamlShortcuts.class).stream().map(ShardingSphereYamlShortcuts::getYamlShortcuts).forEach(yamlShortcuts::putAll);// 将yamlShortcuts中的Construct封装成TypeDescription,添加到Constructor中yamlShortcuts.forEach((key, value) -> addTypeDescription(new TypeDescription(value, key)));this.rootClass = rootClass;}@Overrideprotected final Construct getConstructor(final Node node) {return typeConstructs.getOrDefault(node.getType(), super.getConstructor(node));}@Overrideprotected Class<?> getClassForName(final String className) throws ClassNotFoundException {Preconditions.checkArgument(className.equals(rootClass.getName()), "Class `%s` is not accepted", className);return super.getClassForName(className);}
}

4.1 构造方法

在ShardingSphereYamlConstructor的构造方法中,执行如下:

1)通过Java SPI获取ShardingSphereYamlConstruct,并添加到typeConstructs中;

 Java SPI详见:【源码】Sharding-JDBC源码分析之SPI机制-CSDN博客

ShardingSphereYamlConstruct继承了SnakeYaml的Construct,扩展了getType()接口,用于通过Node.type和该getType()匹配,返回特定的Java对象。

实现类如下:

a)NoneShardingStrategyConfigurationYamlConstruct:无分片策略配置Yaml构造。对于无分片策略,指定返回的配置对象为YamlNoneShardingStrategyConfiguration;

b)ShardingSphereYamlConstructFixture:固定配置的Yaml构造。对于自定义的固定配置,指定返回的配置对象为CustomizedClassFixture;

2)通过Java SPI获取ShardingSphereYamlShortcuts,执行ShardingSphereYamlShortcuts.getYamlShortcuts()方法,将结果添加到yamlShortcuts中;

ShardingSphereYamlShortcuts的实现类如下:

a)ShardingSphereYamlShortcutsFixture:固定快捷方式

b)YamlRuleConfigurationShortcuts:规则快捷方式

2.1)ShardingSphereYamlShortcutsFixture的源码如下:

/*** Yaml快捷的固定配置*/
public final class ShardingSphereYamlShortcutsFixture implements ShardingSphereYamlShortcuts {@Overridepublic Map<String, Class<?>> getYamlShortcuts() {return Collections.singletonMap("!FIXTURE", YamlShortcutsConfigurationFixture.class);}
}

在getYamlShortcuts()方法中,返回key为!FIXTURE,value为YamlShortcutsConfigurationFixture的Map对象。即在配置文件中,标记!FIXTURE之后的配置为YamlShortcutsConfigurationFixture的对应配置。

2.2)YamlRuleConfigurationShortcuts的源码如下:

package org.apache.shardingsphere.infra.yaml.config.shortcut;/*** Yaml规则配置快捷方式*/
public final class YamlRuleConfigurationShortcuts implements ShardingSphereYamlShortcuts {@SuppressWarnings("rawtypes")@Override@SneakyThrows(ReflectiveOperationException.class)public Map<String, Class<?>> getYamlShortcuts() {// 获取所有的规则配置转换器Collection<YamlRuleConfigurationSwapper> swappers = ShardingSphereServiceLoader.getServiceInstances(YamlRuleConfigurationSwapper.class);Map<String, Class<?>> result = new HashMap<>(swappers.size(), 1);for (YamlRuleConfigurationSwapper each : swappers) {// 获取YamlRuleConfigurationSwapper第一个实现接口的第一个泛型的类型Class<?> yamlRuleConfigurationClass = Class.forName(((ParameterizedType) each.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0].getTypeName());// 添加规则标识名字及对应规则配置类。如!SHARDING:YamlShardingRuleConfigurationresult.put(String.format("!%s", each.getRuleTagName()), yamlRuleConfigurationClass);}return result;}
}

2.2.1)通过Java SPI获取YamlRuleConfigurationSwapper,该类为规则配置转换器,可以进行XxxRuleConfiguration和YamlXxxConfiguration的转换。如ShardingTableRuleConfiguration和YamlTableRuleConfiguration的转换。该类有18个实现类。如下:

2.2.2)遍历YamlRuleConfigurationSwapper集合,获取YamlRuleConfigurationSwapper的第一个实现接口的第一个泛型的类型,执行YamlRuleConfigurationSwapper.getRuleTagName()获取规则标识名称,添加到Map集合中,最终返回Map集合。

以下以YamlShardingRuleConfigurationSwapper实现类为例。

package org.apache.shardingsphere.sharding.yaml.swapper;/*** yaml中sharding规则配置转换器*/
public final class YamlShardingRuleConfigurationSwapper implements YamlRuleConfigurationSwapper<YamlShardingRuleConfiguration, ShardingRuleConfiguration> {// 省略其他@Overridepublic Class<ShardingRuleConfiguration> getTypeClass() {return ShardingRuleConfiguration.class;}/*** 返回SHARDING*/@Overridepublic String getRuleTagName() {return "SHARDING";}@Overridepublic int getOrder() {return ShardingOrder.ORDER;}
}

a)获取YamlRuleConfigurationSwapper第一个实现接口的第一个泛型的类型为YamlShardingRuleConfiguration;

b)添加到result中的信息为key=!SHARDING;value=YamlShardingRuleConfiguration

3)遍历yamlShortcuts,生成TypeDescription,执行addTypeDescription()添加到Constructor。即yamlShortcuts中存放了标签及标签对应的类。如!SHARDING:YamlShardingRuleConfiguration,即在yaml中配置的!SHARDING后面的配置信息要解析为YamlShardingRuleConfiguration对象;

详见:Yaml及解析框架SnakeYaml简介及TypeDescription的使用和原理-CSDN博客

4.2 重写Construct getConstructor(final Node node)方法

在该方法比较简单,如果Node的类型在typeConstructs集合中,返回自定义的Construct,否则执行父类的同名方法。

如配置的策略为none,则对应的Node类型为YamlNoneShardingStrategyConfiguration,此时返回的Construct为NoneShardingStrategyConfigurationYamlConstruct,从而指定该Node的配置信息为YamlNoneShardingStrategyConfiguration

小结

限于篇幅,本篇先分享到这里。以下做一个小结:

1)ShardingSphere通过SnakeYaml解析yaml分片策略文件;

2)自定义SnakeYaml的Constructor;

2.1)在构造方法中,添加了自定义的Construct,存放在typeConstructs集合中。如用于处理none的分片策略的Construct;

2.2)在构造方法中,添加了yaml的标签及标签解析成的Java对象。如!SHARDING应该解析为YamlShardingRuleConfiguration对象;

2.3)重写Construct getConstructor(final Node node)方法,如果node的type在typeConstructs集合中,直接返回对应的Construct,否则执行父类的getConstructor(final Node node)方法;

以上为本篇分享的全部内容。

关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【漏洞修复】Tomcat中间件漏洞
  • 强化学习之REINFORECE策略梯度算法——已CartPole环境为例
  • 高级web安全技术(第一篇)
  • 【ARM】v8架构programmer guide(4)_ARMv8的寄存器
  • Oracle(47)如何创建和使用集合?
  • Leetcode面试经典150题-236.二叉树的最低公共祖先
  • 保研考研机试攻略:第二章——入门经典(2)
  • LVS(Linux virual server)
  • 排序算法——插入排序
  • “华为杯”第十六届中国研究生数学建模竞赛-C题:视觉情报信息分析
  • rust pin_project的使用
  • 算法经典题目:Insert Interval
  • 深入了解HTML链接:从基础到进阶——WEB开发系列06
  • C# 不使用 `async` 和 `await` 的常见场景
  • STC-ISP升级MCU
  • 【140天】尚学堂高淇Java300集视频精华笔记(86-87)
  • css布局,左右固定中间自适应实现
  • Docker 笔记(1):介绍、镜像、容器及其基本操作
  • ES6系列(二)变量的解构赋值
  • JavaScript设计模式之工厂模式
  • Java知识点总结(JDBC-连接步骤及CRUD)
  • JS正则表达式精简教程(JavaScript RegExp 对象)
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • Python 基础起步 (十) 什么叫函数?
  • Redis 懒删除(lazy free)简史
  • SegmentFault 技术周刊 Vol.27 - Git 学习宝典:程序员走江湖必备
  • Solarized Scheme
  • vue 配置sass、scss全局变量
  • 不发不行!Netty集成文字图片聊天室外加TCP/IP软硬件通信
  • 前端面试之闭包
  • 区块链将重新定义世界
  • 使用 @font-face
  • 探索 JS 中的模块化
  • 小程序 setData 学问多
  • 写给高年级小学生看的《Bash 指南》
  • TPG领衔财团投资轻奢珠宝品牌APM Monaco
  • 通过调用文摘列表API获取文摘
  • ​必胜客礼品卡回收多少钱,回收平台哪家好
  • # 利刃出鞘_Tomcat 核心原理解析(八)-- Tomcat 集群
  • #### go map 底层结构 ####
  • ()、[]、{}、(())、[[]]等各种括号的使用
  • (+4)2.2UML建模图
  • (1)STL算法之遍历容器
  • (70min)字节暑假实习二面(已挂)
  • (C语言)输入自定义个数的整数,打印出最大值和最小值
  • (rabbitmq的高级特性)消息可靠性
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (附源码)ssm码农论坛 毕业设计 231126
  • (详细文档!)javaswing图书管理系统+mysql数据库
  • .NET 6 在已知拓扑路径的情况下使用 Dijkstra,A*算法搜索最短路径
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .NET MAUI学习笔记——2.构建第一个程序_初级篇
  • .Net Remoting(分离服务程序实现) - Part.3
  • .NET Standard、.NET Framework 、.NET Core三者的关系与区别?
  • .net6 core Worker Service项目,使用Exchange Web Services (EWS) 分页获取电子邮件收件箱列表,邮件信息字段