ShardingSphere源码阅读(一)
ShardingSphere源码阅读前的准备
前言
为什么会选择阅读ShardingSphere的源码呢?其实可以说机缘巧合下,在之前报的课程中有人带,并且是一个小团体进行阅读还有分享的,并且以前没有源码阅读经验,这次参与刚好作为一次开始,大家互相监督可以坚持下去这是最重要的。
引用老师说的一句话:“一个人可以走很快,一群人却可以走很远”,坚持努力总不会错的,共勉!
一、ShardingSphere是什么?
想学习一样新的东西,无非就是以下五步,前四步就够,第五步就是看个人的了
- 首先需知道这个东西的存在,如果连知道都不知道,更不要说学习,所以我知道了ShardingSphere,那么就完成第一步
- 知道了ShardingSphere后,那么就是去了解它到底是干什么的,能干什么的,在官网都能看到
1.1、ShardingSphere官网:https://shardingsphere.apache.org/
1.2、GitHub地址:https://github.com/apache/shardingsphere - 初步了解ShardingSphere的工作原理功能效果后,为了加深印象,就需要根据官方的文档去跑demo,这一步很重要,如果不愿意跑一次demo,那么就没后面的事情的,亲身体验效果才能更容易领悟,也为阅读源码走出第一步。
- 跑完demo之后,就是思考为什么会得到这个结果,这个结果是怎么实现的,这里就是进入源码阅读的入口,需要思考这个过程,带着疑问深入了解,去源码查找答案
- 达到了一定程度,就是需要理解实现原理,这步是拆解整个框架的组成,并且思考着能不能自己也实现一个类似功能的工具,不在本文的范围(其实这里就达到架构师的门槛和成为开源社区项目committer的重要一环)
二、跑Demo
ShardingSphere有JDBC,Proxy和UI三部分,分别看看
1.ShardingSphere-JDBC
ShardingSphere的demo发现了五种不同的类型:
- 分库(SHARDING_DATABASES)
- 分表(SHARDING_TABLES)
- 分库分表(SHARDING_DATABASES_AND_TABLES)
- 读写分离(READWRITE_SPLITTING)
- 分库读写分离(SHARDING_READWRITE_SPLITTING)
- 外加一种特殊的类型:加密(Encrypt_Databases)
1.1.API接口实现
实例化代码如下:
public static DataSource newInstance(final ShardingType shardingType) throws SQLException {
switch (shardingType) {
case SHARDING_DATABASES:
return new ShardingDatabasesConfigurationPrecise().getDataSource();
case SHARDING_TABLES:
return new ShardingTablesConfigurationPrecise().getDataSource();
case SHARDING_DATABASES_AND_TABLES:
return new ShardingDatabasesAndTablesConfigurationPrecise().getDataSource();
case READWRITE_SPLITTING:
return new ReadwriteSplittingConfiguration().getDataSource();
case SHARDING_READWRITE_SPLITTING:
return new ShardingReadwriteSplittingConfigurationPrecise().getDataSource();
default:
throw new UnsupportedOperationException(shardingType.name());
}
}
根据不同的shardingType去选择实例化数据源,里面处理实例化数据源之外,还需要分库分表,读写分离等规则,这些规则决定了如何实现相应的功能,选择分库分表作为解析,代码如下:
private ShardingRuleConfiguration createShardingRuleConfiguration() {
ShardingRuleConfiguration result = new ShardingRuleConfiguration();
//不同表的分表规则,第一个是order表
result.getTables().add(getOrderTableRuleConfiguration());
//这个是order_item表
result.getTables().add(getOrderItemTableRuleConfiguration());
//分表的所有logic表放到这个组中
result.getBindingTableGroups().add("t_order, t_order_item");
result.getBroadcastTables().add("t_address");
result.setDefaultDatabaseShardingStrategy(new StandardShardingStrategyConfiguration("user_id", "inline"));
result.setDefaultTableShardingStrategy(new StandardShardingStrategyConfiguration("order_id", "standard_test_tbl"));
Properties props = new Properties();
//分库算法,具体基于哪个字段作为分库标志
props.setProperty("algorithm-expression", "demo_ds_${user_id % 2}");
result.getShardingAlgorithms().put("inline", new ShardingSphereAlgorithmConfiguration("INLINE", props));
result.getShardingAlgorithms().put("standard_test_tbl", new ShardingSphereAlgorithmConfiguration("STANDARD_TEST_TBL", new Properties()));
//自动key生成使用什么算法,这里使用雪花算法
result.getKeyGenerators().put("snowflake", new ShardingSphereAlgorithmConfiguration("SNOWFLAKE", getProperties()));
return result;
}
private static ShardingTableRuleConfiguration getOrderTableRuleConfiguration() {
ShardingTableRuleConfiguration result = new ShardingTableRuleConfiguration("t_order", "demo_ds_${0..1}.t_order_${[0, 1]}");
result.setKeyGenerateStrategy(new KeyGenerateStrategyConfiguration("order_id", "snowflake"));
return result;
}
private static ShardingTableRuleConfiguration getOrderItemTableRuleConfiguration() {
ShardingTableRuleConfiguration result = new ShardingTableRuleConfiguration("t_order_item", "demo_ds_${0..1}.t_order_item_${[0, 1]}");
result.setKeyGenerateStrategy(new KeyGenerateStrategyConfiguration("order_item_id", "snowflake"));
return result;
}
private static Map<String, DataSource> createDataSourceMap() {
//具体需要绑定多少个数据源,每个数据源都需要写入map
Map<String, DataSource> result = new HashMap<>();
result.put("demo_ds_0", DataSourceUtil.createDataSource("demo_ds_0"));
result.put("demo_ds_1", DataSourceUtil.createDataSource("demo_ds_1"));
return result;
}
private static Properties getProperties() {
Properties result = new Properties();
result.setProperty("worker-id", "123");
return result;
}
其实上述的参数在官网都能找到,也有教如何配置,这个就是ShardingSphere-JDBC的API接口实现,根据官网参数和修改参数去多跑几次Demo,可以更清晰每一个有用的参数,其它的shardingType基本都是大同小异
1.2.配置文件实现
配置文件主要就是yaml(可以写成yml)文件,用配置文件写好数据源和规则,加载配置文件就可以,这些配置属性在ShardingSphere官网也是有的,可以参考官方文档
dataSources:
ds_0:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.jdbc.Driver
jdbcUrl: jdbc:mysql://localhost:3306/demo_ds_0?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
username: root
password:
ds_1:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.jdbc.Driver
jdbcUrl: jdbc:mysql://localhost:3306/demo_ds_1?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
username: root
password:
rules:
# 重点:这个就是控制这个yaml文件实现那个功能的,-!SHARDING 就是分库分表规则
- !SHARDING
tables:
t_order:
actualDataNodes: ds_${0..1}.t_order_${0..1}
tableStrategy:
standard:
shardingColumn: order_id
shardingAlgorithmName: t_order_inline
keyGenerateStrategy:
column: order_id
keyGeneratorName: snowflake
t_order_item:
actualDataNodes: ds_${0..1}.t_order_item_${0..1}
tableStrategy:
standard:
shardingColumn: order_id
shardingAlgorithmName: t_order_item_inline
keyGenerateStrategy:
column: order_item_id
keyGeneratorName: snowflake
bindingTables:
- t_order,t_order_item
broadcastTables:
- t_address
defaultDatabaseStrategy:
standard:
shardingColumn: user_id
shardingAlgorithmName: database_inline
defaultTableStrategy:
none:
shardingAlgorithms:
database_inline:
type: INLINE
props:
algorithm-expression: ds_${user_id % 2}
t_order_inline:
type: INLINE
props:
algorithm-expression: t_order_${order_id % 2}
t_order_item_inline:
type: INLINE
props:
algorithm-expression: t_order_item_${order_id % 2}
keyGenerators:
snowflake:
type: SNOWFLAKE
props:
worker-id: 123
props:
sql-show: false
至于加密Encrypt_Databases其实和其他区别不大,只不过需要注意支持的加密算法,和配置算法的规则,这些在官方文档都可以查到,不展开来讲了
2.ShardingSphere-Proxy
由于ShardingSphere-JDBC每次修改都需要重新package jar包,才能生效,在维护方面不是很便利,所以就出现了ShardingSphere-Proxy,proxy是一个中间件,需要单独启动一个服务才能使用,也是使用配置文件yaml实现功能的,一共需要两份及以上的配置文件
2.1.server.yaml,是用于启动和配置proxy服务的
#mode:
# type: Cluster
# repository:
# type: ZooKeeper
# props:
# namespace: governance_ds
# serverLists: localhost:2181
# retryIntervalMilliseconds: 500
# timeToLiveSeconds: 60
# maxRetries: 3
# operationTimeoutMilliseconds: 500
# overwrite: false
rules:
#重点,这个是proxy的logic表,可以连接的,用这个表关联实体表,统一从这里进行操作就可以了
- !AUTHORITY
users:
- root@:root
- sharding@:sharding
provider:
type: NATIVE
props:
max-connections-size-per-query: 1
executor-size: 16 # Infinite by default.
proxy-frontend-flush-threshold: 128 # The default value is 128.
# LOCAL: Proxy will run with LOCAL transaction.
# XA: Proxy will run with XA transaction.
# BASE: Proxy will run with B.A.S.E transaction.
proxy-transaction-type: LOCAL
proxy-opentracing-enabled: false
proxy-hint-enabled: false
sql-show: false
check-table-metadata-enabled: false
从上面配置看到一大段给注释的配置,看得出是有关集群的,还是使用ZooKeeper进行服务注册发现的
2.2.config-xxx.yaml
这些配置就是关于分库分表,读写分离等的规则的配置文件,可以直接复用ShardingSphere—JDBC配置文件,其实是一致的,如果有修改需要重启
启动proxy前,把所有配置都完成后,直接启动,然后业务代码那边的数据源,配置为proxy的server.yaml的logic库表就可以和正常数据库一样使用了
3.ShardingSphere-UI
在ShardingSphere-Proxy中提到两个点,一是proxy是独立运行的中间件服务,二是留了一个注释的配置,是关于集群的。
有关第一点,单个服务就会有不可用的时候,那么当proxy服务不可用时,业务也不可用了,所以就需要高可用,引入第二点,搭配ZooKeeper实现集群达到高可用,而ShardingSphere-UI就是用可视化界面对上述的进行管理,并且是实时更新配置文件的,刷新后即可用,这是ShardingSphere-JDBC和ShardingSphere-Proxy所不能达到的,具体如下图
总结
先要阅读源码,首先去跑一下官方的Demo,目的是对ShardingSphere有一定的熟悉后,才能开展后续,当然有ShardingSphere的开发使用经验的人可以省略此步,在这个过程中把思考一下怎么实现的,为什么这样实现的,然后记录这些问题,带到后面源码阅读中去查找答案,会发现阅读起来才会有感觉,不然根本不知道如何下手,怎么去阅读,那么就很难坚持下去,而我因为有小组鞭策,并且交流方法,并且找到适合自己阅读源码方式,坚持下了,后面就是关于源码阅读的文章了。带着问题去阅读总不会比其他办法更加差了。