在上一篇中,我们介绍了ShardingSphere中的路由引擎的基本结构,并引出了ShardingRouter这个核心类。我们回顾一下ShardingRouter中route方法的基本流程,如下图所示:
我们已经介绍了上图中六个步骤中的前三个。同时,我们也在《ShardingSphere源码解析之分布式主键》一篇中给出了ShardingSphere中实现分布式主键的详细过程。今天我们继续对上图中的剩余步骤展开讨论。
我们来看ShardingRouter中route方法的第四段代码,如下所示:
//创建分片条件
ShardingConditions shardingConditions = getShardingConditions(parameters, sqlStatementContext, generatedKey.orNull(), metaData.getRelationMetas());
boolean needMergeShardingValues = isNeedMergeShardingValues(sqlStatementContext);
if (sqlStatementContext.getSqlStatement() instanceof DMLStatement && needMergeShardingValues) {
checkSubqueryShardingValues(sqlStatementContext, shardingConditions);
mergeShardingConditions(shardingConditions);
}
这段代码的作用是创建分片条件ShardingCondition,在ShardingSphere中,ShardingCondition定义如下所示,包含了一组路由值(包含Table和Column)和节点值(包含DataSource和Table):
public class ShardingCondition {
private final List<RouteValue> routeValues = new LinkedList<>();
private final Collection<DataNode> dataNodes = new LinkedList<>();
}
获取分片条件的getShardingConditions方法如下所示,可以看到这里根据输入的SQL类型,分别通过InsertClauseShardingConditionEngine和WhereClauseShardingConditionEngine创建了ShardingConditions:
private ShardingConditions getShardingConditions(final List<Object> parameters, final SQLStatementContext sqlStatementContext, final GeneratedKey generatedKey, final RelationMetas relationMetas) {
if (sqlStatementContext.getSqlStatement() instanceof DMLStatement) {
if (sqlStatementContext instanceof InsertSQLStatementContext) {
InsertSQLStatementContext shardingInsertStatement = (InsertSQLStatementContext) sqlStatementContext;
return new ShardingConditions(new InsertClauseShardingConditionEngine(shardingRule).createShardingConditions(shardingInsertStatement, generatedKey, parameters));
}
return new ShardingConditions(new WhereClauseShardingConditionEngine(shardingRule, relationMetas).createShardingConditions(sqlStatementContext.getSqlStatement(), parameters));
}
return new ShardingConditions(Collections.<ShardingCondition>emptyList());
}
对于路由引擎而言,分片条件的主要目的就是提取用于路由的目标数据库、表和列之间的关系,InsertClauseShardingConditionEngine和WhereClauseShardingConditionEngine中的处理逻辑都是为了包含这些关系信息的一组ShardingCondition对象。
当获取这些ShardingCondition之后,我们看到有一个优化的步骤。当可以对ShardingCondition进行合并时,会调用mergeShardingConditions进行合并。这部分优化工作与主线无关,这里就不做具体展开。
接下来我们来看第五段代码,这段代码执行真正的路由,如下所示:
//获取RoutingEngine并执行路由
RoutingEngine routingEngine = RoutingEngineFactory.newInstance(shardingRule, metaData, sqlStatementContext, shardingConditions);
RoutingResult routingResult = routingEngine.route();
if (needMergeShardingValues) {
Preconditions.checkState(1 == routingResult.getRoutingUnits().size(), "Must have one sharding with subquery.");
}
这段代码是ShardingRouter的核心,我们获取了一个RoutingEngine实例,然后基于该实例执行路由并返回一个RoutingResult对象。RoutingEngine定义如下,可以看到只有一个简单的route方法:
public interface RoutingEngine {
RoutingResult route();
}
在ShardingSphere中存在一批RoutingEngine的实现类,其类层结构如下所示:
RoutingEngineFactory工厂类负责生成具体的RoutingEngine,生成逻辑如下所示:
public static RoutingEngine newInstance(final ShardingRule shardingRule, final ShardingSphereMetaData metaData, final SQLStatementContext sqlStatementContext, final ShardingConditions shardingConditions) {
SQLStatement sqlStatement = sqlStatementContext.getSqlStatement();
Collection<String> tableNames = sqlStatementContext.getTablesContext().getTableNames();
//全库路由
if (sqlStatement instanceof TCLStatement) {
return new DatabaseBroadcastRoutingEngine(shardingRule);
}
//全库表路由
if (sqlStatement instanceof DDLStatement) {
return new TableBroadcastRoutingEngine(shardingRule, metaData.getTables(), sqlStatementContext);
}
//阻断路由
if (sqlStatement instanceof DALStatement) {
return getDALRoutingEngine(shardingRule, sqlStatement, tableNames);
}
//全实例路由
if (sqlStatement instanceof DCLStatement) {
return getDCLRoutingEngine(shardingRule, sqlStatementContext, metaData);
}
//默认库路由
if (shardingRule.isAllInDefaultDataSource(tableNames)) {
return new DefaultDatabaseRoutingEngine(shardingRule, tableNames);
}
//全库路由
if (shardingRule.isAllBroadcastTables(tableNames)) {
return sqlStatement instanceof SelectStatement ? new UnicastRoutingEngine(shardingRule, tableNames) : new DatabaseBroadcastRoutingEngine(shardingRule);
}
//默认库路由
if (sqlStatementContext.getSqlStatement() instanceof DMLStatement && tableNames.isEmpty() && shardingRule.hasDefaultDataSourceName()) {
return new DefaultDatabaseRoutingEngine(shardingRule, tableNames);
}
//单播路由
if (sqlStatementContext.getSqlStatement() instanceof DMLStatement && shardingConditions.isAlwaysFalse() || tableNames.isEmpty() || !shardingRule.tableRuleExists(tableNames)) {
return new UnicastRoutingEngine(shardingRule, tableNames);
}
//分片路由
return getShardingRoutingEngine(shardingRule, sqlStatementContext, shardingConditions, tableNames);
}
这些RoutingEngine的具体介绍我们放在接下来的几篇文章中,我们先只需要了解ShardingSphere在包结构的设计上把具体的RoutingEngine分成了五类,即广播(broadcast)路由、混合(complex)路由、默认数据库(defaultdb)路由、无效(ignore)路由、标准(standard)路由以及单播(unicast)路由,如下所示:
RoutingEngine的执行结果是RoutingResult,而RoutingResult中包含了一个RoutingUnit集合,RoutingUnit中的变量定义如下所示,可以看到有两个关于DataSource名称的变量以及一个TableUnit列表:
private final String dataSourceName;
private final String masterSlaveLogicDataSourceName;
private final List<TableUnit> tableUnits = new LinkedList<>();
而TableUnit实际上就是保存着逻辑表名和实际表名,如下所示:
public final class TableUnit {
private final String logicTableName;
private final String actualTableName;
}
当通过一系列的路由引擎处理之后,我们获得了RoutingResult对象,但并不是直接将其进行返回,而是会构建一个SQLRouteResult对象。这就是ShardingRouter的route方法最后一段代码:
//构建SQLRouteResult
SQLRouteResult result = new SQLRouteResult(sqlStatementContext, shardingConditions, generatedKey.orNull());
result.setRoutingResult(routingResult);
if (sqlStatementContext instanceof InsertSQLStatementContext) {
setGeneratedValues(result);
}
return result;
我们来到SQLRouteResult的定义,看看它与RouteResult之间有什么不同,SQLRouteResult中的变量如下所示:
public final class SQLRouteResult {
private final SQLStatementContext sqlStatementContext;
private final ShardingConditions shardingConditions;
private final GeneratedKey generatedKey;
private final Collection<RouteUnit> routeUnits = new LinkedHashSet<>();
private RoutingResult routingResult;
}
可以看到SQLRouteResult中包含了RoutingResult。我们可以认为SQLRouteResult是整个SQL路由返回的路由结果,在后续的流程中还会被PreparedStatementRoutingEngine等高层对象所使用,而RoutingResult只是RoutingEngine返回的路由结果,它的使用者就是位于底层的ShardingRouter。
至此,我们把ShardingRouter类的核心流程做了介绍。在ShardingSphere的路由引擎中,ShardingRouter可以说是一个承上启下的核心类。向下我们可以挖掘各种RoutingEngine的具体实现;向上我们可以延展到读写分离等面向应用的具体场景。在接下来的内容中,我们将分别从这两个维度继续深入探讨ShardingSphere的路由引擎。
更多内容可以关注我的公众号:程序员向架构师转型。