CobarClient源码分析
CobarClient是阿里巴巴公司開發一個的開源的、基于iBatis和Spring的分布式數據庫訪問層。為了支持iBatis,Spring框架提供了一個SqlMapClientTemplate,通過模板模式簡化了在Spring框架中對于iBatis中的使用。而CobarClient則繼承了SqlMapClientTemplate,提供了CobarSqlMapClientTemplate給應用使用。CobarSqlMapClientTemplate和SqlMapClientTemplate繼承關系如下:
在 CobarSqlMapClientTemplate持有的眾多屬性中,比較重要的有cobarDataSourceService(提供多數據源的管理服務)、router(Sql路由接口的實現)、concurrentRequestProcessor(Sql并發執行的命令類),下面就從路由規則到最終的Sql執行來分析一下CobarClient的原理。
根據CobarClient的文檔,CobarClient一共提供了四種路由規則:
- NamespaceShardingRule:針對iBatis中Sql Map定義的,屬于某一個NameSpace的Sql語句執行某一個路由規則,根據路由的結果選擇一個或者多個數據分區。
- Namespace(Only)Rule:針對iBatis中Sql Map定義的,屬于某一個NameSpace的Sql語句無差別的將Sql語句路由到一個或者多個數據分區。
- SqlActionShardingRule:針對iBatis中Sql Map定義的某一個Sql Id對應的Sql語句,執行某一個路由規則,根據路由的結果選擇一個或者多個數據分區。
- SqlAction(Only)Rule:針對iBatis中Sql Map定義的某一個Sql Id對應的Sql語句,無差別的將這個語句路由到一個或者多個數據分區。
?
?
在CobarClient中這四個路由規則,分別對應四個具體的實現類,而這四個實現類,都實現了IRoutingRule這個接口,繼承關系如下所示:
在4種路由規則分別具體對應的實現類中,都實現了isDefinedAt(IBatisRoutingFact routingFact)這個方法,這個方法實現的功能判斷現在要執行的Sql語句是不是可以匹配當前的路由規則實例。這個方法的參數,routingFact的類型是IBatisRoutingFact,代表的是一個路由上下文信息:
public class IBatisRoutingFact { private String action; // SQL identity private Object argument; // the argument of SQL action } 舉個例子,看看IBatisNamespaceRule類中isDefinedAt方法的實現如下: public boolean isDefinedAt(IBatisRoutingFact routingFact) { Validate.notNull(routingFact); String namespace = StringUtils.substringBeforeLast(routingFact.getAction(), "."); return StringUtils.equals(namespace, getTypePattern()); }實際完成的工作很簡單,就是看看當前的Sql Id的NameSpace是否和規則定義的NameSpace一致,如果一致,表示使用該路由規則。清楚了路由規則的實現之后,那么這么多路由規則又是怎么匯總到CobarSqlMapClientTemplate中的呢?根據CobarClient文檔中的說明:
默認情況下, CobarClientInternalRouter將接收4組不同類型的路由規則, 但路由規則的類型對于用戶來說實際上是不必要的, 所以, 為了避免用戶過多的糾纏于CobarClientInternalRouter的實現細節, 我們給出了針對CobarClientInternalRouter配置的一個Spring的FactoryBean實現, 以幫助簡化CobarClientInternalRouter的配置, 該FactoryBean實現類為com.alibaba.cobar.client.router.config.CobarInteralRouterXmlFactoryBean。CobarInteralRouterXmlFactoryBean將根據指定的xml形式的配置文件中的內容, 自動構建不同類型的路由規則, 然后注入到它將最終返回的CobarClientInternalRouter實例之上。 而讀取, 解析配置信息, 并構建不同類型路由規則等 “瑣事” 將完全對用戶透明。一個典型的路由規則文件可能如下
<rules> <rule> <namespace>com.alibaba.cobar.client.entity.Follower</namespace> <shards>partition1</shards> </rule> <rule> <sqlmap>com.alibaba.cobar.client.entity.Follower.create</sqlmap> <shards>p1, p2</shards> </rule> <rule> <sqlmap>com.alibaba.cobar.client.entity.Follower.create</sqlmap> <shardingExpression>id>10000 and id< 20000</shardingExpression> <shards>p1, p2</shards> </rule> <rule> <namespace>com.alibaba.cobar.client.entity.Follower</namespace> <shardingExpression>id>10000 and id< 20000</shardingExpression> <shards>p1, p2</shards> </rule> </rules> 解析了完所有的路由規則配置文件之后,CobarClientInternalRouter類的實例將持有一個List,該List的定義如下: private List<Set<IRoutingRule<IBatisRoutingFact, List<String>>>> ruleSequences實際上,這個List只有4個元素,每一個元素是一個集合,分別對應之前提到的四種路由規則。而每個集合中的元素都是一個具體的規則實現類。當具體執行一個Sql語句之前,CobarSqlMapClientTemplate通過它持有的CobarClientInternalRouter實例的doRoute方法來實現具體的路由規則選擇,可見CobarClientInternalRouter實際上充當了一個門面(Facade)的作用,匯總了所有的路由信息,而且CobarSqlMapClientTemplate是一個更高層的門面。
?? 在doRoute方法中,有兩層循環,第一層循環,遍歷ruleSequence這個List,第二層循環遍歷List中每個Set
在這里最終調用之前提到的isDefinedAt方法,兩次遍歷了ruleSequence之后,將獲得一個Map,
SortedMap<String, DataSource> resultMap 表示的這次Sql語句執行需要用對應的數據源。
2.Sql的執行
?? 當CobarSqlMapClientTemplate通過CobarClientInternalRouter獲取到了本次Sql執行對應的數據源之后,將進入到Sql執行的階段。在Spring框架中,提供了一個接口SqlMapClientCallback接口,CobarSqlMapClientTemplate在每一次執行Sql語句之前,都會生成一個SqlMapClientCallback的內部類,該內部類實現了SqlMapClientCallback接口中定義的回調函數,doInSqlMapClient。例如,在某一次查詢語句之前,對應的內部類可能是:
具體到Sql語句的執行,CobarClient命令模式結合回調函數的方式來實現,標準的命令模式結構圖如下:
在CobarSqlMapClientTemplate中,擔任ConcreteCommand角色的是IConcurrentRequestProcessor,它的執行方法為:
List<Object> process(List<ConcurrentRequest> requests)不同的是,為了支持Sql并行執行,這里傳入的不是單個的request,而是由多個ConcurrentRequest類型的request組成的List(這里的ConcurrentRequest可以看成命令模式的中的Receiver),不同的是在ConcurrentRequest類中是通過多線程加調用SqlMapClientCallback中的回調函數的方式來實現命令模式中Receiver的action方法的。在IConcurrentRequestProcessor調用process方法之后,會通過多線程的方式來執行SqlMapClientCallback中的回調函數,
request.getExecutor().submit(new Callable<Object>() { public Object call() throws Exception { try { return executeWith(connection, action); } finally { latch.countDown(); } } } )executeWith中connection具體的數據源的連接,action就是之前提到的匿名內部類,在executeWith方法中,會調用這個內部的回調函數:
protected Object executeWith(Connection connection, SqlMapClientCallback action) { SqlMapSession session = getSqlMapClient().openSession(); try { try { session.setUserConnection(connection); } catch (SQLException e) { throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", e); } try { return action.doInSqlMapClient(session); } catch (SQLException ex) { throw new SQLErrorCodeSQLExceptionTranslator().translate("SqlMapClient operation", null, ex); } } finally { session.close(); } }當執行完SqlMapClientCallback中的doInSqlMapClient方法之后,CobarSqlMapClientTemplate還會對多個Sql語句執行獲取的結果進行簡單的合并之后在返回給應用程序,至此,整個Sql的分布式數據庫執行才算結束。
3. 一點體會
CobarClient擴展了Spring提供的SqlMapClientTemplate,在Sql執行之前根據數據路由規則獲取了真正要執行的數據分區,在Sql執行之后在進行結果集的簡單合并,最終實現了分布式數據庫的數據路由功能,但是CorBarClient不是沒有缺點
???????? 我們可以根據情況提供不同的ICobarRouter實現類, 比如Cobar Client默認提供的com.alibaba.cobar.client.router.CobarClientInternalRouter和 om.alibaba.cobar.client.router.DefaultCobarClientInternalRouter, 或者如果路由規則數量很多, 為了保證性能, 也可以實現基于Rete等算法的實現類等. 沒有特殊需求的情況下,我們默認采用CobarClientInternalRouter作為CobarSqlMapClientTemplate使用的 默認Router實現. 但用戶也可以根據情況選用DefaultCobarClientInternalRouter, 二者的使用是類似的。DefaultCobarClientInternalRouter在CobarClientInternalRouter的基礎 上, 對路由規則的匹配進行了分組優化, 通過配置時期的復雜度換取運行時期的簡單高效。 如果規則很多的話,可以考慮使用DefaultCobarClientInternalRouter。
??????? 之前代碼分析的CobarClientInternalRouter的doRoute實現,在兩層的循環中,如果有n個具體的路由規則,那么最壞的情況,要執行n次路由判斷,算法的效率是O(n)。文檔說DefaultCobarClientInternalRouter的doRoute實現效率更高(但是DefaultCobarClientInternalRouter的doRoute方法沒有用到cache,而CobarClientInternalRouter用到了),但實際上只是對于ruleSequences根據Namespace做了分組,那么當有n個路由規則,m個namespace的時候,算法的效率是O(n/m)。由于在每個sql語句執行之前,都需要執行doRoute方法來路由,所以如果用HashMap來保存Sql Id和路由規則的對應關系,算法的效率將提高到O(1),這樣算法的時間消耗不會隨著路由規則的增多而增長。
private String action了。Rule文件中的namespace到了具體的規則類之中,就變成了private String typePatten。這種情況充斥于整個代碼之中,于是最后你能發現,ICobarRouter的默認實現類是CobarClientInternalRouter而不是DefaultCobarClientInternalRouter。
瑕不掩瑜,如果需要一個輕量級的,支持分布式數據庫的數據訪問層框架,并且應用不需要對于查詢的結果集做過于復雜的排序聚會等操作,CobarClient是一個不錯的選擇。
轉載于:https://www.cnblogs.com/javanerd/p/3551831.html
總結
以上是生活随笔為你收集整理的CobarClient源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 6174
- 下一篇: 【转】PBOC3.0和PBOC2.0标准