天天看點

阿裡通信基礎技術架構介紹

在阿裡通信必零(計費重構)項目中,在完成項目建設的同時,我們把日常常用的一些工具類進行了抽取沉澱,形成了基礎庫。我覺得有些東西挺好用的,寫篇文章介紹一下。 二方庫:

<code>&lt;dependency&gt; &lt;groupId&gt;com.alicom&lt;/groupId&gt; &lt;artifactId&gt;alicom-frame&lt;/artifactId&gt; &lt;version&gt;1.0.7&lt;/version&gt; &lt;/dependency&gt;</code>

主要内容包括:

1、 DAO處理,靈活友善的分庫分表政策,可以支援自由自定義分庫分表規則

2、 通過注解,實作方法級緩存,通過dimaond配置控制緩存更新

3、 利用tair實作分布式鎖

4、 jmx支援,通過注解友善的jmx支援

5、 通信用戶端,包括:http/https,ftp/ftps,sftp,oss

6、 其它一些工具類,主要包括:

a、FrameDiamondUtil:Diamond配置讀取,自動監聽diamond的配置變更,儲存在内部的CACHE,不需要調用者寫Listener

b、FrameBeanFactory:便捷的擷取spring bean

c、FrameTimeUtil:便捷的時間處理函數,線程安全,基于joda封裝的時間處理類,性能快

d、JacksonUtil:使用jackson進行json處理;EncryptUtil:3des加減密;Md5Util:md5摘要生成

接下去就其中幾塊内容展開說明一下。

a、首先是datasource配置,這個是必須得,也少不了。我們一般都是用TDDL的Group層,在這配置對應資料源的appName和dbGroupKey。有幾個資料源就配置幾個bean

阿裡通信基礎技術架構介紹

b、編寫我們的sqlmap檔案,xxxxx1_sqlmap.xml,xxxxx2_sqlmap.xml,一個表一個sqlmap,在sqlMapConfig中,對sqlmap檔案進行引用

阿裡通信基礎技術架構介紹

c、配置sqlMap操作類sqlMapClient或sqlMapClientTemplate,需要配置sqlMapConfig和DataSources屬性,一個DataSource需要配一個sqlMapClient

阿裡通信基礎技術架構介紹

d、編寫DAO類,根據不同的操作,sqlMapClient或sqlMapClientTemplate執行對應的sqlmap語句;

如果涉及分庫分表,就需要做些加工。分表意味着sqlmap中對應的表名是由外部參數傳遞建構的,我們一般會寫TableRoute之類的東西,根據規則拼接表名;分庫本質上就使用不同DataSource的sqlMapClient.,我們一般會寫DbRoute之類的東西,根據規則定位sqlMapClient。

當然涉及事務,還需要增加一些事務相關的配置處理。傳統的方式,問題主要有:

• 配置多而繁瑣 

• 分庫分表規則的支援上不靈活,分庫分表對DAO代碼的侵入很大

a、配置檔案

•persistence.xml:資料源datasource相關的配置還是少不了的,有關sequence,事務的配置原來該怎麼樣還是怎麼樣。

但不再需要手動配置sqlMapClient或SqlMapClientTemplate,隻需要設定DAO路徑和資料源映射關系,對于分庫隻要加通配DBNAME就可以了。

阿裡通信基礎技術架構介紹

•sql-map-config.xml和sql-map-null.xml檔案(基本不用改,所有項目都一樣)

一個是 sqlMapConfig全局配置,一個是隻有schema的檔案,用于内部構造作為模闆。

b、命名和目錄約定

所有DAO檔案在dao目錄下,所有DO檔案在dataobject目錄下,目錄平行;

DAO實作類以xxxx DAOImpl命名,對應的DO類是xxxxDO。

為什麼有這個約定,後續會有解釋。

c、DAO實作類繼承BaseDAO,通過注解指定sqlMap檔案,自動注冊為bean。

阿裡通信基礎技術架構介紹

d、DO實作繼承BaseDO,通過@DbDefine 指定分表規則,通過@TableDefine指定分表規則。

我們一般使用代碼生成工具建構DAO/DO、SQLMAP代碼,這些都全自動化了。

e、對于分庫分表特别介紹一下

•sqlmap檔案簡單調整,需要将表名變更為_TABLE_NAME_ ,如:

阿裡通信基礎技術架構介紹

•在DO類上加注解@TableDefine

阿裡通信基礎技術架構介紹

[]裡的内容可以約定哪個字段作為分表key,如何進行key轉換。如上面就是 以gmt_create作為分表鍵,分表轉換規則見monthTableConvert類

另外可以用多個[]号,指定更為複雜的分表規則,如: @TableDefine("zw_add_month_[monthTableConvertByBillMonth]_[acctIdTableConvert]")

•如果不涉及分庫,通過前面說的DAO路徑和DataSource映射就可以擷取最終DS了。

•如果涉及分庫,根據映射規則拿到的是帶通配符DBNAME的DS名。需要在DO類上加注解@DbDefine,用法同@ TableDefine,通過注解指定分庫key和分庫轉換方法類,最終替換通配符,擷取真正DS.

•DAO實作方法,可以通過DO、Query、Map對象向最終的sqlmap傳遞參數,如果涉及分庫分表,對象中一定要包含分表key字段。

•AbstractConvert

所有分庫分表規則的抽象類,具體分庫分表規則可以通過實作這個類來提供。

如:按ID取模分64個表、按ID取模分4個庫、按時間戳按月分表、按時間戳按日分表、按時間戳按年分表等等。

提供了兩個方法需要實作:

String getDefaultKey() 如果對外注解沒指定分庫或分表key,就預設用這個key。

String convert(String key,Object) 具體如何轉換分庫或分表邏輯。

•BaseDO

主要提供了set_TABLE_NAME_(),供分表使用。

•BaseDAO

所有DAO操作基類 

初始化的時候:

根據@SQLMap注解,擷取sqlmap路徑;

找到同級的DO(前面約定的DAO和DO命名規範),根據是否有@DbDefine注解,判斷是否分庫和分庫規則,根據是否有@TableDefine注解,判斷是否分表和分表規則;

sql操作的時候,根據getTemplate方法,擷取SqlMapClientTemplate。

主要操作包括:如果非分庫,直接根據包路徑映射擷取ds名,否則根據分庫規則替換通配符,擷取ds,根據ds建構資料源(第一次是建構,後續會從Map中取);如果是分表的,根據分表規則擷取最終表名,最終通過操作對象傳遞到sqlmap。

Diamond真是個好東西,阿裡内部很多動态配置的業務場景都是由Diamond實作的。有關Diamond的介紹,大家可以自己搜。

Diamond使用主要兩個方法

•Diamond.getConfig(dataId,group,timeoutMs) 

•Diamond. addListener(dataId,group,new ManagerListenerAdapter()

{ public void receiveConfigInfo(String configInfo) { …}

為此我們一般都需要在使用Diamond的類裡加init方法,調用getConfig擷取相應配置,并且指派給我們的業務變量;同時增加一個監聽器,實作receiveConfigInfo方法,當diamond配置變更的時候替換我們得業務變量。

我們覺得這樣使用,每個用到Diamond的類都需要添加類似代碼,似乎不太優雅。能不能封裝一個工具類,直接通過靜态方法就可以擷取Diamond配置值,而有關Diamond推送變更相關代碼也隐式的做到,對調用者透明呢?

FrameDiamondUtil就是這樣一個工具類。提供了

public static String getConfig(String dataId,String group,long timeoutMs)靜态方法,外部使用者隻需要調用這個靜态方法簡單指派,其它都不用管。

如果希望傳回的不是原生配置的String内容 ,而是自己做些轉換,可以調用

public static Object getConfig(String dataId,String group,long timeoutMs,Convert convert),自己實作Convert類就可以。

其實也挺簡單的,内置一個Map

調用getConfig的時候,以group^dataId作key,從Map中取。如果是第一次調用,調用Diamond.getConfig擷取,以及注冊監聽器。

阿裡通信基礎技術架構介紹

在監聽器代碼中,對Map内容進行替換

阿裡通信基礎技術架構介紹

利用AOP,将緩存實作和業務邏輯分離,相信大家都有所耳聞。比如采用SpringCache架構,通過注解就能實作方法級緩存。

我們這套架構就是基于spring Cache做了些擴充,配合Diamond可以定時更新緩存而無需重新開機。

通過spring的注解實作方法級緩存 ;支援緩存到本地JVM或者Tair中,本地緩存遵循LRU政策;通過diamond對緩存生命周期進行管理

使用舉例:

a、聲明注解

阿裡通信基礎技術架構介紹

b、配置檔案,指定需要管理的Diamond及cacheManager配置

阿裡通信基礎技術架構介紹

c、隻要在方法上加注解,就可以實作緩存功能

阿裡通信基礎技術架構介紹

d、dimaond管理緩存生命周期

當時間大于配置内容,就重新整理緩存(其實是替換緩存的key),但外部感覺就像緩存重新整理

阿裡通信基礎技術架構介紹

a、整體上依賴spring Cache

b、實作了spring Cache的Cache接口

本地緩存使用google的concurrentlinkedhashmap,記憶體超出的時候使用LRU;

通過配置确定是否使用tair,如果配置了enableTair,本地讀取不到的時候讀tair,put的時候同時put本地和tair。

c、對SpringCache的KeyGenerator進行了特殊實作

一般大家用SpringCache的時候都是采用DefaultKeyGenerator,通過方法名和參數名建構Key.(當然也有通過表達式自定義key的)。

我們對KeyGenerator做了特殊實作,key的組成除了方法名和參數名外,将從Diamond相關配置中讀取的時間戳作為key組成部分的字首,如果Diamond内容發生了變更,相應的key就變化,這個時候根據改key就取不到緩存,會觸發真正的方法調用,等到第二次調用的時候緩存key沒有變化,那就能讀到緩存。

基于tair的原子操作實作的分布式鎖。

a、實作TairLockObject

舉例CreditAcctIdLock,确定lock key的字首KEY_PREFIX,确定配置項CONFIG_PREFIX;構造函數中傳入acctId

阿裡通信基礎技術架構介紹

b、使用鎖

建構對象 AcctIdLock lock = new AcctIdLock(6536182776198L) ;

擷取鎖 lock.acquire();

釋放鎖 lock.release()。

相對比較簡單,依賴tair的原子操作。

TairLockObject決定了key的組成,決定了如何擷取tair配置(如:資料庫或配置檔案),此外還包括擷取鎖的一些政策:如擷取鎖失敗的重試次數和等待時間。

加鎖:基于 key做 tairManager.incr

釋放鎖:基于key做tairManager.delete

唠唠叨叨講了一些,該收場了。個人覺得在項目實施的過程中,除了完成特定業務目标外,還能逐漸沉澱出一些技術架構,供後續工程使用,避免重複造輪子,還是挺有意義的。