一、前言
最近工作不忙閑來無事,仔細分析了公司整個項目架構,發現用到了很多阿里巴巴集團開源的框架,今天要介紹的是中間件diamond.
二、diamond學習筆記
1、diamond簡介
diamond是一個管理持久配置(持久配置是指配置數據會持久化到磁盤和數據庫中)的系統。無可厚非,淘寶內部正在使用diamond,在淘寶內部的絕大多數系統的配置都是由diamond統一管理的。diamond最大的特點就是簡單、可靠、易用。diamond的簡單是指diamond整體結構非常簡單,從而減少了出錯的可能性;diamond的可靠是指應用方在任何情況下都可以啟動,例如:淘寶的核心系統最初一年多是由diamond所管理,在這期間並沒有發生什么大的故障;diamond的易用是指客戶端使用只需要兩行代碼,暴露出的接口都非常簡單,易於理解。
對於應用系統而言,diamond為其提供獲取配置的服務,應用不僅可以在啟動時從diamond獲取相關的配置,而且可以在運行中對配置數據的變化進行感知並獲取變化后的配置數據。
2、快速使用
源碼檢出:http://code.taobao.org/svn/diamond/trunk。
server搭建:
a. mysql
mysql的安裝(安裝步驟請自行查閱資料,本人建議按照mysql官方文檔),以root用戶登錄,建立用戶並賦予權限,建立數據庫,然后建表。腳本如下:
- create database diamond;
- grant all on diamond.* to CK@'%' identified by 'abc';
- use diamond
- create table config_info (
- 'id' bigint(64) unsigned NOT NULL auto_increment,
- 'data_id' varchar(255) NOT NULL default ' ',
- 'group_id' varchar(128) NOT NULL default ' ',
- 'content' longtext NOT NULL,
- 'md5' varchar(32) NOT NULL default ' ',
- 'gmt_create' datetime NOT NULL default '2010-05-05 00:00:00',
- 'gmt_modified' datetime NOT NULL default '2010-05-05 00:00:00',
- PRIMARY KEY ('id'),
- UNIQUE KEY 'uk_config_datagroup' ('data_id','group_id')
- );
完成后,請將數據庫的配置信息(IP,用戶名,密碼)添加到diamond-server工程的src/resources/jdbc.properties文件中的db.url,db.user,db.password屬性上面,這里建立的庫名,用戶名和密碼,必須和jdbc.properties中對應的屬性相同。
b. tomcat
tomcat是diamond server的運行容器,而對於tomcat的安裝請自行查閱資料,推薦使用tomcat7和安裝tomcat的官方文檔。tomcat安裝后,不需要做任何改動。
c. diamond server
在diamond-server源代碼根目錄下,執行mvn clean package -Dmaven.test.skip,成功后會在diamond-server/target目錄下生成diamond-server.war(如果沒有安裝maven,請參考maven官方文檔進行安裝)。打包完成后,將diamond-server.war放在tomcat的webapps目錄下。啟動tomcat,即啟動了diamond-server。
d. http server
http server用來存放diamond server等地址列表,可以選用任何http server,這里以tomcat為例。一般來講,http server和diamond server是部署在不同機器上的,這里簡單起見,將二者部署在同一個機器下的同一個tomcat的同一個應用中,注意,如果部署在不同的tomcat中,端口號一定是8080,不能修改(所以必須部署在不同的機器上)。在上一步的tomcat的webapps中的diamond-server中建立文件diamond,文件內容是diamond-server的地址列表,一行一個地址,地址為IP,例如:127.0.0.1。完成以上4步后,server端的搭建就完成了。
發布數據:
diamond發布數據通過手工的方式進行。在瀏覽器中輸入http://ip:8080/diamond-server/,ip為server搭建的第二步中的地址,以user為用戶名,123為密碼,登錄后進入后台管理界面,然后點擊“配置信息管理”—— “添加配置信息”,在輸入框中輸入dataId、group、內容,最后點擊“提交”即可。成功后,可以在“配置信息管理”中查詢到發布的數據。
訂閱數據:
diamond客戶端API主要提供了訂閱數據的功能:
a. 客戶端獲取服務端地址
獲取服務端地址對客戶端是透明的,客戶端僅僅需要在本地進行如下域名綁定即可:ip a.b.c,ip為前面搭建的http-server的ip。
b. 創建訂閱者
- DiamondManager manager = new DefaultDiamondManager(group, dataId, new ManagerListener() {
- public Executor getExecutor() {
- return null;
- }
- public void receiveConfigInfo(String configInfo) {
- // 客戶端處理數據的邏輯
- }
- });
參數說明:group和dataId為String類型,二者結合為diamond-server端保存數據的惟一key。ManagerListener 是客戶端注冊的數據監聽器, 它的作用是在運行中接受變化的配置數據,然后回調receiveConfigInfo()方法,執行客戶端處理數據的邏輯。如果要在運行中對變化的配置數據進行處理,就一定要注冊ManagerListener。
c. 獲取配置數據
- String configInfo = manager.getAvailableConfigureInfomation(timeout);
diamond-server端保存的配置全都為文本類型,返回給客戶端的配置數據為java.lang.String類型,timeout為從網絡獲取配置數據的超時時間。客戶端調用每次調用該方法,都能夠保證獲取一份最新的可用的配置數據。
2、核心原理
diamond核心原理主要包括server集群的數據同步、client獲取server地址、client從server獲取數據、client運行時感知server的數據變化,這四部分。
a. server集群的數據同步
diamond-server將數據存儲在mysql和本地文件中,mysql是一個中心,diamond認為存儲在mysql中的數據絕對正確,除此之外,server會將數據存儲在本地文件中。
同步數據有兩種方式:
server寫數據時,先將數據寫入mysql,然后寫入本地文件,寫入完成后發送一個HTTP請求給集群中的其他server,其他server收到請求,從mysql中dump剛剛寫入的數據至本地文件。
server啟動后會啟動一個定時任務,定時從mysql中dump所有數據至本地文件。
b. client獲取server地址
diamond-client在使用時沒有指定server地址的代碼,地址獲取對用戶是透明的。server地址存儲在一台具有域名的機器上的HTTP server中,我們稱它為地址服務器,diamond-client使用前需要在本地進行正確的域名綁定,啟動時它會根據域名綁定,去對應環境的地址服務器上獲取diamond-server地址列表。獲取的地址列表,會保存在client本地,當出現網絡異常,無法從網絡獲取地址列表時,client會使用本地保存的地址列表。client啟動后會啟動一個定時任務,定時從HTTP server上獲取地址列表並保存在本地,以保證地址是最新的。
c. client從server獲取數據
client調用getAvailableConfigInfomation(), 即可獲取一份最新的可用的配置數據,獲取過程實際上是拼接http url,使用http-client調用http method的過程。為了避免短時間內大量的獲取數據請求發向server,client端實現了一個帶有過期時間的緩存,client將本次獲取到的數據保存在緩存中,在過期時間內的所有請求,都返回緩存內的數據,不向server發出請求。
d. client運行時感知server的數據變化
這是diamond最為核心的一個功能。這個特性是通過比較client和server的數據的MD5值實現的。server在啟動時,會將所有數據的MD5加載到內存中(MD5根據某算法得出,保證數據內容不同,MD5不同,MD5存儲在mysql中),數據更新時,會更新內存中對應的MD5。client在啟動並第一次獲取數據后,會將數據的MD5保存在內存中,並且在啟動時會啟動一個定時任務,定時去server檢查數據是否變化。每次檢查時,client將MD5傳給server,server比較傳來的MD5和自身內存中的MD5是否相同,如果相同,說明數據沒變,返回一個標示數據不變的字符串給client;如果不同,說明數據變了,返回變化數據的dataId和group給client. client收到變化數據的dataId和group,再去server請求一次數據,拿回數據后回調監聽器。
3、diamond架構
diamond服務是一個集群,是一個去除單點的協作集群。如下圖所示:
對該圖進行一些說明:
a. 作為一個配置中心,diamond的功能分為發布和訂閱兩部分。因為diamond存放的是持久數據,這些數據的變化頻率不會很高,甚至很低,所以發布采用手工的形式,通過diamond后台管理界面發布;訂閱是diamond的核心功能,訂閱通過diamond-client的API進行。
b. diamond服務端采用mysql加本地文件的形式存放配置數據。發布數據時,數據先寫到mysql,再寫到本地文件;訂閱數據時,直接獲取本地文件,不查詢數據庫,這樣可以最大程度減少對數據庫的壓力。
c. diamond服務端是一個集群,集群中的每台機器連接同一個mysql,集群之間的數據同步通過兩種方式進行,一是每台server定時去mysql dump數據到本地文件,二是某一台server接收發布數據請求,在更新完mysql和本機的本地文件后,發送一個HTTP請求(通知)到集群中的其他幾台server,其他server收到通知,去mysql中將剛剛更新的數據dump到本地文件。
d. 每一台server前端都有一個nginx,用來做流量控制。
e. 圖中沒有將地址服務器畫出,地址服務器是一台有域名的機器,上面運行有一個HTTP server,其中有一個靜態文件,存放着diamond服務器的地址列表。客戶端啟動時,根據自身的域名綁定,連接到地址服務器,取回diamond服務器的地址列表,從中隨機選擇一台diamond服務器進行連接。
4、容災機制
diamond容災機制涉及到client和server兩部分,主要包括以下幾個方面:
a. server存儲數據的方式
server存儲數據是“數據庫 + 本地文件”的方式,集群間的數據同步我們在之前的文章中講過(請參考專題二的原理部分),client訂閱數據時,訪問的是本地文件,不查詢數據庫,這樣即使數據庫出問題了,仍然不影響client的訂閱。
b. server是一個集群
這是一個基本的容災機制,集群中的一台server不可用了,client發現后可以自動切換到其他server上進行訪問,自動切換在client內部實現。
c. client保存snapshot
client每次從server獲取到數據后,都會將數據保存在本地文件系統,diamond稱之為snapshot,即數據快照。當client下次啟動發現在超時時間內所有server均不可用(可能是網絡故障),它會使用snapshot中的數據快照進行啟動。
d. client校驗MD5
client每次從server獲取到數據后,都會進行MD5校驗(數據保存在response body,MD5保存在response header),以防止因網絡故障造成的數據不完整,MD5校驗不通過直接拋出異常。
e. client與server分離
client可以和server完全分離,單獨使用,diamond定義了一個“容災目錄”的概念,client在啟動時會創建這個目錄,每次主動獲取數據(即調用getAvailableConfigInfomation()方法),都會優先從“容災目錄”獲取數據,如果client按照一個固定的規則,在“容災目錄”下配置了需要的數據,那么client直接獲取到數據返回,不再通過網絡從diamond-server獲取數據。同樣的,在每次輪詢時,都會優先輪詢“容災目錄”,如果發現配置還存在於其中,則不再向server發出輪詢請求。 以上的情形, 會持續到“容災目錄”的配置數據被刪除為止。
根據以上的容災機制,我們可以總結一下diamond整個系統完全不可用的條件:
數據庫不可用;
所有server均不可用;
client主動刪除了snapshot;
client沒有備份配置數據,導致其不能配置"容災目錄";
本人在公司的線上環境仔細分析過,同時滿足這四點條件的概率那是相當小!
三、總結
通過對diamond源碼的閱讀及架構的分析,可以得出這樣一個結論:diamond簡單、可靠、易用的特點是相輔相成的,即diamond之所以簡單是因為使用的都是一些最常用的技術以及產品,它之所以表現得非常穩定,跟其架構簡單是分不開的,當然,穩定的另一個主要原因是它具備一套比較完善的容災機制。