前面我們花了幾篇文章對各種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中讀寫分離相關內容,值得作爲一個專題進行專門講解,我們放到後面的文章中做具體展開。
更多內容可以關注我的公衆號:程序員向架構師轉型。