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

前面我们花了几篇文章对各种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中读写分离相关内容,值得作为一个专题进行专门讲解,我们放到后面的文章中做具体展开。

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

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

猜你喜欢

转载自blog.csdn.net/lantian08251/article/details/104690574