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中讀寫分離相關內容,值得作爲一個專題進行專門講解,我們放到後面的文章中做具體展開。

更多內容可以關注我的公衆號:程序員向架構師轉型。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章