ShardingSphere源码解析之路由引擎(二)

上一篇中,我们介绍了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的路由引擎。

更多内容可以关注我的公众号:程序员向架构师转型。

发布了112 篇原创文章 · 获赞 12 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/lantian08251/article/details/104587657
今日推荐