前面我们花了几篇文章对各种RouteEngine做了全面介绍,但这不是ShardingSphere路由引擎模块的全部。让我们回到《ShardingSphere源码解析之路由引擎(一)》中所描述的如下所示的整体结构图:
上图省略了RouteEngine相关的一大块类图,而是更加关注于路由引擎的全貌。实际上,全面讲到的内容都属于ShardingRouter之下的内容,属于底层引擎。而今天我们将要关注更加上层的结构,其中PreparedStatementRoutingEngine和StatementRoutingEngine属于中间层组件,和ShardingRouter一样位于sharding-core-route工程中。而BaseShardingEngine及其它们的子类PreparedQueryShardingEngine和SimpleQueryShardingEngine则位于sharding-core-entry工程中,处于更加上层的位置。
我们的思路仍然是从下往上,先来看PreparedStatementRoutingEngine和StatementRoutingEngine。其中StatementRoutingEngine的实现如下所示:
public final class StatementRoutingEngine {
private final ShardingRouter shardingRouter;
private final ShardingMasterSlaveRouter masterSlaveRouter;
public StatementRoutingEngine(final ShardingRule shardingRule, final ShardingSphereMetaData metaData, final SQLParseEngine sqlParseEngine) {
shardingRouter = new ShardingRouter(shardingRule, metaData, sqlParseEngine);
masterSlaveRouter = new ShardingMasterSlaveRouter(shardingRule.getMasterSlaveRules());
}
public SQLRouteResult route(final String logicSQL) {
SQLStatement sqlStatement = shardingRouter.parse(logicSQL, false);
return masterSlaveRouter.route(shardingRouter.route(logicSQL, Collections.emptyList(), sqlStatement));
}
}
可以看到在StatementRoutingEngine的route方法中,通过ShardingMasterSlaveRouter对通过ShardingRouter所生成的SQLRouteResult进行了再一次路由,也就是说在分片路由的基础上添加了主从路由,关于主从路由我们会在下一篇文章中进行讨论。
我们再来看PreparedStatementRoutingEngine,其实现方法如下所示:
public final class PreparedStatementRoutingEngine {
private final String logicSQL;
private final ShardingRouter shardingRouter;
private final ShardingMasterSlaveRouter masterSlaveRouter;
private SQLStatement sqlStatement;
public PreparedStatementRoutingEngine(final String logicSQL, final ShardingRule shardingRule, final ShardingSphereMetaData metaData, final SQLParseEngine sqlParseEngine) {
this.logicSQL = logicSQL;
shardingRouter = new ShardingRouter(shardingRule, metaData, sqlParseEngine);
masterSlaveRouter = new ShardingMasterSlaveRouter(shardingRule.getMasterSlaveRules());
}
public SQLRouteResult route(final List<Object> parameters) {
if (null == sqlStatement) {
sqlStatement = shardingRouter.parse(logicSQL, true);
}
return masterSlaveRouter.route(shardingRouter.route(logicSQL, parameters, sqlStatement));
}
}
可以看到PreparedStatementRoutingEngine的整体流程与StatementRoutingEngine比较类似,最终都是依赖于ShardingMasterSlaveRouter所实现的最终路由。这里只有两个较小的区别,其中一个是PreparedStatementRoutingEngine对sqlStatement对象做了一层复用,只有在第一次路由时才会进行SQL解析。另一个是它的route方法的输入是一个参数对象parameters,而不是logicSQL。
现在我们来到sharding-core-entry工程,看看更上层的处理流程。整个sharding-core-entry工程只有三个类,即作为基类的BaseShardingEngine以及两个子类PreparedQueryShardingEngine和SimpleQueryShardingEngine。我们先来看BaseShardingEngine类,它本质上是一个模板类。BaseShardingEngine的shard方法如下所示:
public SQLRouteResult shard(final String sql, final List<Object> parameters) {
List<Object> clonedParameters = cloneParameters(parameters);
SQLRouteResult result = executeRoute(sql, clonedParameters);
result.getRouteUnits().addAll(HintManager.isDatabaseShardingOnly() ? convert(sql, clonedParameters, result) : rewriteAndConvert(sql, clonedParameters, result));
boolean showSQL = shardingProperties.getValue(ShardingPropertiesConstant.SQL_SHOW);
if (showSQL) {
boolean showSimple = shardingProperties.getValue(ShardingPropertiesConstant.SQL_SIMPLE);
SQLLogger.logSQL(sql, showSimple, result.getSqlStatementContext(), result.getRouteUnits());
}
return result;
}
在这里我们看到了SQL转换(Convert)和改写(Rewrite)的入口,这是路由引擎之外的执行流程,我们今天不做展开。上述代码与路由相关最核心的就是executeRoute方法,如下所示:
private SQLRouteResult executeRoute(final String sql, final List<Object> clonedParameters) {
routingHook.start(sql);
try {
SQLRouteResult result = route(sql, clonedParameters);
routingHook.finishSuccess(result, metaData.getTables());
return result;
// CHECKSTYLE:OFF
} catch (final Exception ex) {
// CHECKSTYLE:ON
routingHook.finishFailure(ex);
throw ex;
}
}
这个方法的处理模式与我们在《ShardingSphere源码解析之SQL解析引擎(一)》中介绍的SQLParseEngine的parse方法有着类似的代码结构,同样用到了钩子机制。在SQLParseEngine用的是ParsingHook,而这里用到的是SPIRoutingHook。SPIRoutingHook实现了RoutingHook,而RoutingHook定义如下所示:
public interface RoutingHook {
void start(String sql);
void finishSuccess(SQLRouteResult sqlRouteResult, TableMetas tableMetas);
void finishFailure(Exception cause);
}
在ShardingSphere中,RoutingHook接口只有一个实现类,即SPIRoutingHook(和RoutingHook接口一起位于sharding-core-route工程的org.apache.shardingsphere.core.route.hook),代码如下所示:
public final class SPIRoutingHook implements RoutingHook {
private final Collection<RoutingHook> routingHooks = NewInstanceServiceLoader.newServiceInstances(RoutingHook.class);
static {
NewInstanceServiceLoader.register(RoutingHook.class);
}
@Override
public void start(final String sql) {
for (RoutingHook each : routingHooks) {
each.start(sql);
}
}
@Override
public void finishSuccess(final SQLRouteResult sqlRouteResult, final TableMetas tableMetas) {
for (RoutingHook each : routingHooks) {
each.finishSuccess(sqlRouteResult, tableMetas);
}
}
@Override
public void finishFailure(final Exception cause) {
for (RoutingHook each : routingHooks) {
each.finishFailure(cause);
}
}
}
可以看到这里使用了NewInstanceServiceLoader实现了RoutingHook的SPI实例注册和加载,这应该是SPIRoutingHook这个类命名的来源。而从实现上看,SPIRoutingHook虽然实现了RoutingHook的各个方法,但更像是RoutingHook的一个聚合器,把所有的RoutingHook实例加载进来并依次执行它们所实现的接口方法,我们可以从上面的代码中和明确的看到这一点。
作为一个模板类,BaseShardingEngine提供了如下所示的两个模板方法供子类进行实现:
protected abstract List<Object> cloneParameters(List<Object> parameters);
protected abstract SQLRouteResult route(String sql, List<Object> parameters);
我们先来看SimpleQueryShardingEngine中的实现,如下所示:
@Override
protected List<Object> cloneParameters(final List<Object> parameters) {
return Collections.emptyList();
}
@Override
protected SQLRouteResult route(final String sql, final List<Object> parameters) {
return routingEngine.route(sql);
}
显然,对于SimpleQueryShardingEngine而言,不需要参数,所以cloneParameters直接返回空列表。而route方法则直接使用前面介绍的StatementRoutingEngine进行路由。
PreparedQueryShardingEngine类的实现也非常类似,传入了参数并使用PreparedStatementRoutingEngine进行路由,如下所示:
@Override
protected List<Object> cloneParameters(final List<Object> parameters) {
return new ArrayList<>(parameters);
}
@Override
protected SQLRouteResult route(final String sql, final List<Object> parameters) {
return routingEngine.route(parameters);
}
至此,关于ShardingSphere路由引擎部分的内容基本都介绍完毕。作为总结,我通过下面所示的时序图来梳理这些路由的主流程(以PreparedQueryShardingEngine为例)。
注意到本文中还提到了ShardingMasterSlaveRouter类,关于ShardingMasterSlaveRouter的介绍涉及到ShardingSphere中读写分离相关内容,值得作为一个专题进行专门讲解,我们放到后面的文章中做具体展开。
更多内容可以关注我的公众号:程序员向架构师转型。