阿裏通信基礎技術框架介紹
該文章來自於阿裏巴巴技術協會(ATA)精選文章。
一、總體
在阿裏通信必零(計費重構)項目中,在完成項目建設的同時,我們把日常常用的一些工具類進行了抽取沉澱,形成了基礎庫。我覺得有些東西挺好用的,寫篇文章介紹一下。 二方庫:
<dependency>
<groupId>com.alicom</groupId>
<artifactId>alicom-frame</artifactId>
<version>1.0.7</version>
</dependency>
主要內容包括:
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摘要生成
接下去就其中幾塊內容展開說明一下。
二、DAO操作,方便的分庫分表支持
1、先來說一下規規矩矩的spring-ibatis如何實現DAO操作
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代碼的侵入很大
2、我們提供了什麼
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字段。
3、如何實現
•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工具類FrameDiamondUtil
1、Diamond的普通使用方式
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配置變更的時候替換我們得業務變量。
2、我們提供了什麼
我們覺得這樣使用,每個用到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類就可以。
3、如何實現
其實也挺簡單的,內置一個Map
調用getConfig的時候,以group^dataId作key,從Map中取。如果是第一次調用,調用Diamond.getConfig獲取,以及注冊監聽器。
在監聽器代碼中,對Map內容進行替換
四、通過注解,實現方法級緩存
1、解決什麼問題
利用AOP,將緩存實現和業務邏輯分離,相信大家都有所耳聞。比如采用SpringCache框架,通過注解就能實現方法級緩存。
有關SpringCache的介紹,可以見 https://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/
我們這套框架就是基於spring Cache做了些擴展,配合Diamond可以定時更新緩存而無需重啟。
2、我們提供了什麼
通過spring的注解實現方法級緩存 ;支持緩存到本地JVM或者Tair中,本地緩存遵循LRU策略;通過diamond對緩存生命周期進行管理
使用舉例:
a、聲明注解
b、配置文件,指定需要管理的Diamond及cacheManager配置
c、隻要在方法上加注解,就可以實現緩存功能
d、dimaond管理緩存生命周期
當時間大於配置內容,就刷新緩存(其實是替換緩存的key),但外部感覺就像緩存刷新
3、如何實現
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沒有變化,那就能讀到緩存。
五、 分布式鎖
1、說明
基於tair的原子操作實現的分布式鎖。
2、使用方式
a、實現TairLockObject
舉例CreditAcctIdLock,確定lock key的前綴KEY_PREFIX,確定配置項CONFIG_PREFIX;構造函數中傳入acctId
b、使用鎖
構建對象 AcctIdLock lock = new AcctIdLock(6536182776198L) ;
獲取鎖 lock.acquire();
釋放鎖 lock.release()。
3、怎麼實現
相對比較簡單,依賴tair的原子操作。
TairLockObject決定了key的組成,決定了如何獲取tair配置(如:數據庫或配置文件),此外還包括獲取鎖的一些策略:如獲取鎖失敗的重試次數和等待時間。
加鎖:基於 key做 tairManager.incr
釋放鎖:基於key做tairManager.delete
六、總結
嘮嘮叨叨講了一些,該收場了。個人覺得在項目實施的過程中,除了完成特定業務目標外,還能逐步沉澱出一些技術框架,供後續工程使用,避免重複造輪子,還是挺有意義的。
最後更新:2017-04-01 13:37:10
上一篇:
Spring MVC前後端中文編碼解碼問題
下一篇:
Redis數據結構
展開關於紅警,大家在熱議 紅警2共和國(水樹奈(閆(中華人民共和國稅收征收管理法(主席令第四十九號) 2015年8月15日 - 會關於修改〈中華人民共和國文物保護法〉等十二部法律的決定》(主席令第...第八十九條 納稅人、扣繳義務人可以委托稅務代理人代為辦
centos 徹底刪除nodejs默認安裝文件
《配置管理最佳實踐》——2.13 結論
ioctl,unlocked_ioctl 處理方法
阿裏雲服務器ECS格式化和掛載普通雲數據盤(最新版)
走進阿裏雲:做雲數據、大計算的No.1
VxWorks啟動流程
基於數加平台全民眾籌平台開啟大數據時代
【雲和恩墨大講堂電子期刊】挑戰者:Google成功的背後
AppPoolService-IIS應用程序池輔助類(C#控製應用程序池操作)