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