1. 程式人生 > >[從原始碼學設計]螞蟻金服SOFARegistry之推拉模型

[從原始碼學設計]螞蟻金服SOFARegistry之推拉模型

# [從原始碼學設計]螞蟻金服SOFARegistry之推拉模型 [toc] ## 0x00 摘要 SOFARegistry 是螞蟻金服開源的一個生產級、高時效、高可用的服務註冊中心。 本系列文章重點在於分析設計和架構,即利用多篇文章,從多個角度反推總結 DataServer 或者 SOFARegistry 的實現機制和架構思路,讓大家藉以學習阿里如何設計。 本文為第七篇,介紹SOFARegistry網路操作的推拉模型。 ## 0x01 相關概念 **Push還是Pull???** ### 1.1 推模型和拉模型 在觀察者模式中,又分為推模型和拉模型兩種方式。  **推模型**:主題物件向觀察者推送主題的詳細資訊,不管觀察者是否需要,推送的資訊通常是主題物件的全部或部分資料。 **拉模型**:主題物件在通知觀察者的時候,只傳遞少量資訊。如果觀察者需要更具體的資訊,由觀察者主動到主題物件中獲取,相當於是觀察者從主題物件中拉資料。 具體兩個模型詳細剖析如下: #### 1.1.1 推模型: ##### 特點: - 基於客戶器/伺服器機制、由伺服器主動將資訊送到客戶器的技術; - “推”的方式是指,Subject維護一份觀察者的列表,每當有更新發生,Subject會把更新訊息主動推送到各個Observer去。 - 伺服器把資訊送給客戶器之前,並沒有明顯的客戶請求,push事務由伺服器發起; - 主題物件向觀察者推送主題的詳細資訊,不管觀察者是否需要,推送的資訊通常是主題物件的全部或部分資料。 - 推模型是假定主題物件知道觀察者需要的資料; ##### 優點: - push模式可以讓資訊主動、快速地尋找使用者/客戶器,資訊的主動性和實時性比較好。 - 高效。如果沒有更新發生,不會有任何更新訊息推送的動作,即每次訊息推送都發生在確確實實的更新事件之後,都是有意義的。 - 實時。事件發生後的第一時間即可觸發通知操作。 - 可以由Subject確立通知的時間,可以避開一些繁忙時間。 - 可以表達出不同事件發生的先後順序 ##### 缺點: - 精確性較差,可能推送的資訊並不一定滿足客戶的需求。推送模式不能保證能把資訊送到客戶器; - 因為推模式採用了廣播機制,如果客戶器正好聯網並且和伺服器在同一個頻道上,推送模式才是有效的; - push模式無法跟蹤狀態,採用了開環控制模式,沒有使用者反饋資訊; - 不管觀察者是否需要,推送的資訊通常是主題物件的全部或部分資料; #### 1.1.2 拉模型 ##### 特點: - 是由客戶器主動發起的事務。伺服器把自己所擁有的資訊放在指定地址(如IP、port),客戶器向指定地址傳送請求,把自己需要的資源“拉”回來; - “拉”的方式是指,各個Observer維護各自所關心的Subject列表,自行決定在合適的時間去Subject獲取相應的更新資料; - 拉模型是主題物件不知道觀察者具體需要什麼資料,沒有辦法的情況下,乾脆把自身傳遞給觀察者,讓觀察者自己去按需要取值; ##### 優點: - 不僅可以準確獲取自己需要的資源,還可以及時把客戶端的狀態反饋給伺服器; - 如果觀察者眾多,Subject來維護訂閱者的列表可能困難或者臃腫,這樣可以把訂閱關係解脫到Observer去完成; - Observer可以不理會它不關心的變更事件,只需要去獲取自己感興趣的事件即可; - Observer可以自行決定獲取更新事件的時間; - 拉的形式可以讓Subject更好地控制各個Observer每次查詢更新的訪問許可權; ##### 缺點: - 最大的缺點就是不及時; ### 1.2 Guava LoadingCache Guava是Google guava中的一個記憶體快取模組,用於將資料快取到JVM記憶體中。實際專案開發中經常將一些公共或者常用的資料快取起來方便快速訪問。 Google Guava Cache提供了基於容量,時間和引用的快取回收方式。基於容量的方式內部實現採用LRU演算法,基於引用回收很好的利用了Java虛擬機器的垃圾回收機制。 其中的快取構造器CacheBuilder採用構建者模式提供了設定好各種引數的快取物件,快取核心類LocalCache裡面的內部類Segment與jdk1.7及以前的ConcurrentHashMap非常相似,都繼承於ReetrantLock,還有六個佇列,以實現豐富的本地快取方案。 ## 0x02 業務領域 ### 2.1 應用場景 SOFARegistry 的業務特點如下: - SOFARegistry 系統分為三個叢集,分別是元資料叢集 MetaServer、會話叢集 SessionServer、資料叢集 DataServer。 - DataServer,SessionServer,MetaServer 本質上都是網路應用程式; - 複雜的系統有多個地方需要考慮到一致性問題,比如當服務 Publisher 上下線或者斷連時,相應的資料會通過 SessionServer 註冊到 DataServer 中。此時,DataServer 的資料與 SessionServer 會出現短暫的不一致性; - SOFARegistry 針對不同模組的一致性需求採取了不同的方案。對於 MetaServer 模組來說,採用了強一致性的 Raft 協議來保證叢集資訊的一致性。對於資料模組來說,SOFARegistry 選擇了 AP 保證可用性,同時保證了最終一致性; ### 2.2 問題點 我們通過業務,能夠想到的問題點如下: - 新訊息資料需要即時更新,想做到秒級的通知,這一般來說需要推模型; - 但是推模型難以確保穩定性; - 推模式,客戶端程式碼簡單,由服務端進行推送資料,省去了客戶端無謂的輪詢類操作。但是需要服務端複雜化推送邏輯。 - 拉模式,需要自行維護偏移量,負載均衡等; ### 2.3 解決方案 對於以上的問題點,業界一般來說會採用“推”和“拉”結合的形式,例如,服務端只負責通知 “某一些資料已經準備好”,至於是否需要獲取和什麼時候客戶端來獲取這些資料,完全由客戶端自行確定。 ### 2.4 阿里方案 我們首先看看阿里都應用了什麼方案。 #### 2.4.1 各種模型應用 在SOFARegistry‘中,應用了**各種模型
**,比如 : - **SessionServer 和 DataServer 之間的通訊,是基於推拉結合的機制** - 推:DataServer 在資料有變化時,會主動通知 SessionServer,SessionServer 檢查確認需要更新(對比 version) 後主動向 DataServer 獲取資料。 - 拉:除了上述的 DataServer 主動推以外,SessionServer 每隔一定的時間間隔(預設30秒),會主動向 DataServer 查詢所有 dataInfoId 的 version 資訊,然後再與 SessionServer 記憶體的 version 作比較,若發現 version 有變化,則主動向 DataServer 獲取資料。這個“拉”的邏輯,主要是對“推”的一個補充,若在“推”的過程有錯漏的情況可以在這個時候及時彌補。 - **SOFARegistry 服務發現模式採用的是推拉結合方式
。** - 客戶端訂閱資訊釋出到服務端時可以進行一次地址列表查詢,獲取到全量資料,並且把對應的服務 ID 版本資訊儲存在 Session 回話層,後續如果服務端釋出資料變更,通過服務 ID 版本變更通知回話層 Session,Session 因為儲存客戶端訂閱關係,瞭解哪些客戶端需要這個服務資訊,再根據版本號大小決定是否需要推送給這個版本較舊的訂閱者,客戶端也通過版本比較確定是否更新本次推送的結果覆蓋記憶體。 - 此外,為了避免某次變更通知獲取失敗,定期還會進行版本號差異比較,定期去拉取版本低的訂閱者所需的資料進行推送保證資料最終一致。 - **Client 與 SessionServer 之間,完全基於推的機制
** - SessionServer 在接收到 DataServer 的資料變更推送,或者 SessionServer 定期查詢 DataServer 發現數據有變更並重新獲取之後,直接將 dataInfoId 的資料推送給 Client。如果這個過程因為網路原因沒能成功推送給 Client,SessionServer 會嘗試做一定次數(預設5次)的重試,最終還是失敗的話,依然會在 SessionServer 定期每隔 30s 輪訓 DataServer 時,會再次推送資料給 Client。 下面是兩種場景的資料推送對比圖。 ![img](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9uaWJPWnBhUUt3MDhsYWxKYlg1RkJFUVEzUDE2V1pKRzhaY0VFVWVrRmdMb2tJTElTZVNCdG5UdjRITDJaRTMzWFM1Sm5ITDhLVkc3V1RseXpiVXpnb1EvNjQw?x-oss-process=image/format,png) #### 2.4.2 推拉模型 就本文涉及的問題域來說,螞蟻金服在這裡採用了經典的推拉模型來維持資料一致性,下面我們僅以 `Session Server和 Data Server 之間維護資料一致性` 為例說明。大致邏輯如下: SOFARegistry 中採用了 `LoadingCache` 的資料結構來在 SessionServer 中快取從 DataServer 中同步來的資料。 - **拉模型**: - 每個 cache 中的 entry 都有過期時間,在拉取資料的時候可以設定過期時間(預設是 30s); - 這個過期時間使得 cache 定期去 DataServer 查詢當前 session 所有 sub 的 dataInfoId,對比如果 session 記錄的最近推送version(見`com.alipay.sofa.registry.server.session.store.SessionInterests#interestVersions` )比 DataServer 小,說明需要推送; - 然後 SessionServer 主動從 DataServer 獲取該 dataInfoId 的資料(此時會快取到 cache 裡),推送給 client; - 這個“拉”的邏輯,主要是對“推”的一個補充,若在“推”的過程有錯漏的情況可以在這個時候及時彌補。 - **推模型**: - 當 DataServer 中有資料更新時,也會主動向 SessionServer 發請求使對應 cache entry 失效; - 當SessionServer 檢查確認需要更新(對比 version) 之後,主動向 DataServer 獲取資料; - SessionServer去更新失效 entry。 ## 0x03 拉模型 in Session Server 這裡 SOFARegistry 採用了 Guava LoadingCache 的資料結構,通過給 cache 中的 entry 設定過期時間的方式,使得 cache 定期從 DataServer 中拉取資料以替換過期的 entry。 模型大致示例如下,下文會詳細講解: ```java +-----------------------------------------+ | Session Server | | | | +-------------------------------------+ | | | SessionCacheService | | | | | | | | +--------------------------------+ | | | | | | | | | | | Load