1. 程式人生 > >淘寶Diamond架構分析

淘寶Diamond架構分析

背景知識

早期的應用都是單體的,配置修改後,只要通過預留的管理介面重新整理reload即可。後來,應用開始拆分,從單一系統拆分成多個子系統,每個子系統還會對應多個執行例項,就開始面臨一些問題:
1. 配置分散在多個業務子系統裡,對同一配置的翻譯在多個子系統裡經常不一致。比如訂單和購物車都有貨幣型別的配置,如果購物車上了一種新的貨幣型別而訂單卻沒有相應同步增加配置項就會造成程式錯誤。
2. 將配置收斂成一個公有服務,可以有效改善,但是又會帶來其他問題。在複雜應用裡,修改一個配置項,無法確切的知道需要重新整理哪些相關子系統。最終只能做全量重新整理,甚至是停機發布。這對於一些停機敏感的應用例如電商幾乎是無法接受的。
3. 配置收斂後,配置中心成了應用中的單點,配置如果掛了,應用也會跟著產生異常甚至掛掉。

Diamond就是為了解決這些問題,它是個高可用的配置中心。

Diamond的配置型別

配置是Diamond的核心域,也是Diamond致力於去解決的問題。Diamond有兩個主要配置型別– single和aggr。二者結構如下:
配置結構
Aggr和single相比,少md5多datumId。DatumId是aggr的邏輯主鍵,aggr下dataId和datumId是1對多的關係,也就是說多條aggr會聚合成一條single,diamond通過merge任務對aggr合併最終生成一條single。

Md5是對content md5編碼生成的字串,用於判斷快取資料相比資料庫資料是否不同,快取資料必須嚴格與資料庫資料一致,diamond並沒有資料版本,預設資料庫資料是最新的,也就是說如果資料庫資料發生回退,即使快取資料更新也會跟著回退。

Single才有md5,aggr其實並不算是完整的配置(多條aggr一起才是一個完整的配置),所以不需要校驗資料是否改變。

整體架構設計

下圖是Diamond的元件檢視。Diamond主要有ops, sdk, client和server 4個元件。Ops是運維用的配置工具,主要用於下發以及查詢配置等;server則是Diamond的後臺,處理配置的一些邏輯;sdk則是提供給ops或者其他第三方應用的開發工具包;client則是程式設計api,它和sdk乍看有點像,其實差別很大,sdk是用於構建前臺運維配置程式的,本質是對資料的維護,所有的訪問和操作都是直接面向資料庫的;而client則是這些資料的消費者,事實上準確的說是diamond的消費者們(各子系統)都是通過client元件對server訪問。
程序檢視

Diamond server是無中心節點的邏輯叢集,讀請求都是訪問local file,而寫請求則會先進入資料庫,接著再更新各節點快取。注意:ops或者其他第三方運維繫統(其實就是sdk模組)讀取和寫入的都是資料庫,這很容易理解,快取會有lag,配置系統必須面向的是實時資料。

Diamond的資料庫是單點的,這就可以利用資料庫特性保證資料的原子性,一致性和永續性,也就不需要實現類似zk的叢集協議,也就不存在leader/follower以及observer等節點角色,它是去中心化的,所有節點都可以接受任意請求。Diamond是典型的讀多寫少,寫一般都來自運維繫統例如ops,這種請求量會很小,即使峰值期對資料庫的衝擊也不會太大。實際上它就是資料庫之上的一個保護殼,資料庫的資料通過它透出來,也通過它滲進去。

Diamond的同質節點之間會相互通訊以保證資料的一致性,每個節點都有其它節點的地址資訊,其中一個節點收到變更請求後,首先寫入資料庫,再通知所有同質節點更新快取,保證資料的一致性。

為了保證高可用,client會在app端以本地檔案形式快取資料的snapshot,保證即使server不可用時app也可用,這一點和dubbo很相似,所以也完全可以使用diamond搭建dubbo註冊中心。

記憶體快取

  • Client端使用的記憶體cache是一個AtomicReference

    1. 它並不是通常理解的記憶體快取,而只是一個事件源,只有被監聽的配置才會有cache。Cache內聚了group,dataId,md5,content和listener等。
    2. 客戶端的長輪詢任務(下一節將會重點介紹)只輪詢被監聽的配置,也就是cache的資料。客戶端在pull到新資料後首先會更新snapshot,再更新cache,接著全量對比所有cache和它關聯的listener的md5資訊從而知道配置更新有沒有被通知,沒有則以cache中的內容作為訊息載體通知,通知完成後更新listener的md5。
    3. 沒被監聽的資料不需要輪詢,因為diamond提供的讀資料api預設會先從服務節點獲取實時資料。
  • 在客戶端發起長輪詢或者服務節點做dump時,都需要對比md5資訊以確定是否要推送或者dump。Server端快取全量快取了所有配置的md5資訊,並會第一時間得到更新,得到更新同時還會推送LocalDataChangeEvent。

  • 無論客戶端還是服務端,記憶體快取僅僅是為了滿足某種功能需求,並不作為讀的資料來源(客戶端只快取部分資料,服務端不快取配置內容)。這是基於產品本身定位而來的,產品定位本身就是犧牲一部分速度以降低成本,並且同時提供長輪詢機制為時效性要求高的配置做到準實時的變更推送。但在客戶端,每個應用的興趣點都是分散的,平均下來每個應用感興趣的配置資料並不大。

長輪詢

Client會不斷長輪詢server,獲取最新的配置推送,儘量保證本地資料的時效性。
長輪詢

Client預設啟動週期任務對server進行長輪詢感知server的配置變化,server感知到配置變化就傳送變更的資料編號,客戶端通過資料編號再去拉取最新配置資料;否則超時結束請求(預設10秒)。拉取到新配置後,client會通知監聽者(MessageListener)做相應處理,使用者可以通過Diamond#addListener監聽。
長輪詢客戶端
服務端通過AsyncContext對請求做非阻塞處理,通過定時任務感知配置變化。
長輪詢server
詳細描述下上圖,
1. server收到請求後啟動AsyncContext,並基於它構造ClientLongPulling。ClientLongPulling除了AsyncContext還有一個超時回覆任務對長輪詢請求做超時處理
2. 之後ClientLongPulling被加入等待列表。LongPullingService關聯一個感知資料變化的定時任務,當有資料變化時(收到LocalDataChangeEvent),就會迴圈等待列表裡的ClientLongPulling,推送資料變化回客戶端。
4. Dump是抽象出來的一塊兒概念,server的資料變化都會觸發響應的dump task,並會發送相應的事件,由server感知,DataChangeTask也是一個事件監聽者,能感知local file的資料變化。

服務端架構設計

Diamond叢集是去中心化的,使用通知機制保證叢集各節點資料一致。
叢集資料同步
1. Diamond叢集內每個節點都有其他節點的地址資訊,當一臺節點收到寫請求後首先寫資料庫,接著就會發送ConfigDataChangeEvent,監聽者收到該事件後通知所有節點做資料變更。通知的所有節點也包括自己,下發配置的請求只會更新資料庫,並不會更新本地檔案快取。
2. 通知傳送到所有節點後,通過dump更新local file。Dump是將配置物件dump進本地檔案的過程,dump完畢後傳送LocalDataChangeEvent,見上節。

任務管理

Diamond收到配置請求,先執行資料庫操作,然後再向工作管理員的任務棧裡插入一條任務,工作管理員感知到新任務後,pick相應的TaskProcessor處理。TaskProcessor和Task的關係通過TaskManager#addProcessor(或setDefaultProcessor)設定。
Diamond的工作管理員是FIFO的,這會造成長延時任務阻塞其他任務的執行。為解決這個問題,Diamond的開發者為每個Task都定製TaskManager。使用者可以做些優化,參考hadoop的公平演算法,針對應用場景(比如長延時,離線,實時等等)定製TaskProcessor。
任務管理

其他一些任務

為了保證Diamond的資料一致,除了以上介紹的兩類任務,還有其他一些task,見下圖:
server心跳任務
1. Merge任務用於合併aggr。App下發聚合配置到diamond後,會觸發合併任務生成single,同時廣播配置變更事件(ConfigDataChangeEvent),各節點收到通知後拉取資料庫相應single資料並更新local file。這與上面描述的server資料同步基本類似,只是事件源頭換成了merge。
merge任務
2. DumpAllTask會每6小時run一次做全量dump,全量刪除老的快取資料,生成新快取。
2. DumpChangeTask做增量dump。通過和資料庫的配置做md5對比,刪除被移除配置的檔案快取,更新md5不一致的檔案快取等等。
3. 清除歷史資料用於清除1周前的資料庫his表資料。
4. 心跳任務用於記錄心跳時間,節點服務重啟時會判斷距離上次心跳時間是否已經超過一小時,超過一小時則做全量dump,否則做增量。

剩下的都很好理解,不再一一介紹,需要注意的是,這些任務並不是都是定時做,有些是事件觸發的,例如DumpTask和merge;還有些在Diamond服務啟動時會觸發,如merge,DumpChangeTask等。