SOFAStack (Scalable Open Financial Architecture Stack) 是螞蟻金服自主研發的金融級雲原生架構,包含了建構金融級雲原生架構所需的各個元件,是在金融場景裡錘煉出來的最佳實踐。
SOFARegistry 是螞蟻金服開源的具有承載海量服務注冊和訂閱能力的、高可用的服務注冊中心,在支付寶/螞蟻金服的業務發展驅動下,近十年間已經演進至第五代。
本文為《剖析 | SOFARegistry 架構》第六篇,本篇作者子懿,來自阿裡雲。《剖析 | SOFARegistry 架構》系列由 SOFA 團隊和源碼愛好者們出品,項目代号:,文末包含往期系列文章。
GitHub 位址:
https://github.com/sofastack/sofa-registry前言
微服務架構為了保證所有服務可用,當服務發生問題時能及時摘除有問題的服務,需要定期檢測服務可用性即健康檢測。健康檢測包括用戶端心跳和服務端主動探測兩種方式,定期發送 TCP 或 HTTP 請求根據響應确定服務是否正常。服務注冊中心提供服務注冊和訂閱服務,在服務釋出者服務資訊發生變化、或者節點上下線時通知變更,動态更新消費方的服務位址清單資訊,支援服務注冊和下線的快速變更通知。
本文重點圍繞服務的健康檢測、SOFARegistry 的健康檢測以及基于 SOFARegistry 實作秒級服務注冊下線等方面剖析 SOFARegistry 如何實作秒級服務上下線通知原理,闡述如何使用 SOFARegistry 對于服務的注冊下線場景通過推送機制快速實作端到端的傳達功能:
- 如何實作服務的健康檢測?業界服務注冊中心的健康機制是怎樣的?SOFARegistry 的健康檢測實作方式?
- SOFARegistry 服務注冊下線資料流轉過程是怎樣的?SOFARegistry 内部角色如何實作秒級服務上下線通知?
服務的健康檢測
服務的健康檢測是如何實作?健康檢測分為用戶端心跳和服務端主動探測兩種方式:
- 用戶端心跳
- 用戶端采取每隔一定時間間隔主動發送心跳方式向服務端表明自己的服務狀态正常,心跳是 TCP 或者 HTTP 的形式;
- 通過維持用戶端和服務端的 Socket 長連接配接自己實作用戶端心跳的方式;
- ZooKeeper 沒有主動的發送心跳,而是依賴元件本身提供的臨時節點的特性,通過 ZooKeeper 連接配接的 Session 維持臨時節點;
- 用戶端心跳中長連接配接的維持和用戶端的主動心跳偏重服務鍊路是否正常,不一定是服務狀态正常;服務端主動調用服務健康檢查是比較準确的方式,通過傳回結果成功判斷服務狀态健康情況;
- 服務端主動探測
- 服務端調用服務釋出者 HTTP 接口來完成健康檢測;
- 對于沒有提供 HTTP 服務的 RPC 應用,服務端調用服務釋出者的接口來實作健康檢測;
- 通過執行腳本形式來進行定時檢測;
- 服務端主動探測依然存在問題。服務注冊中心主動調用 RPC 服務的某個接口無法做到通用性;在很多場景下服務注冊中心到服務釋出者的網絡是不通的,服務端無法主動發起健康檢查;
注冊中心的健康檢測
業界服務注冊中心的健康檢測機制:
- Eureka:定期有 Renew 心跳,資料具有 TTL(Time To Live);并且支援自定義 HealthCheck 機制,當 HealthCheck 檢測出系統不健康時主動更新 Instance 的狀态;
- Zookeeper:定期發送連接配接心跳以保持會話 (Session),會話本身 (Session) 具有TTL;
- Etcd:定期通過 HTTP 對資料進行 Refresh,資料具有 TTL。申請 Lease 租約,設定服務生存周期TTL;
- Consul:Agent 定期對服務進行 healthcheck,支援 HTTP/TCP/Script/Docker;由服務主動定期向 agent 更新 TTL;
SOFARegistry 的健康檢測
業界服務注冊中心的健康檢測都有個共同的關鍵詞:“定期”。定期檢測的時間周期通常設定為秒級,比如 3 秒、5 秒或 10 秒,甚至更長,也就是說服務的健康狀态總是滞後的。螞蟻金服的注冊中心從最初的版本設計開始,就把健康狀态的及時感覺,當做一個重要的設計目标,特别是需要做到“服務當機能被及時發現”。是以 SOFARegistry 在健康檢測的設計方面決定“服務資料與服務釋出者的實體連接配接綁定在一起,斷連馬上清資料”,簡稱此特點叫做連接配接敏感性。連接配接敏感性是指在 SOFARegistry 裡所有 Client 都與 SessionServer 保持長連接配接,每條長連接配接都設定基于 SOFABolt 的連接配接心跳,如果長連接配接斷連用戶端立即發起重建立連,時刻保持 Client 與 SessionServer 之間可靠的連接配接。
SOFARegistry 将服務資料 (PublisherRegister) 和服務釋出者 (Publisher) 的連接配接的生命周期綁定在一起:每個 PublisherRegister 定義屬性 connId,connId 由注冊本次服務的 Publisher 的連接配接辨別 (IP 和 Port)構成,也就是隻要該 Publisher 和 SessionServer 斷連,服務資訊資料即失效。用戶端重建立連成功後重新注冊服務資料,重新注冊的服務資料會被當成新的資料,考慮更換長連接配接後 Publisher 的 connId 是 Renew 新生成的。
譬如當服務的程序當機時,一般情況下 OS 立刻斷開程序相關的連接配接(即發送 FIN),是以 SessionServer 能夠實時感覺連接配接斷開事件,然後把該 connId 相關的所有 PublisherRegister 都清除,并且及時推送給所有服務訂閱者 (Subscriber)。如果隻是網絡問題導緻連接配接斷開,實際的服務程序沒有當機,此時用戶端立即發起重新連接配接 SessionServer 并且重新注冊所有服務資料。對服務訂閱者本身來說接收到的是服務釋出者經曆短暫的服務下線後以及再次重新上線。假如此過程耗時足夠短暫(例如 500ms 内發生斷連和重連),服務訂閱者可能感受不到服務下線,因為 DataServer 内部的資料通過 mergeDatum 延遲合并變更的 Publisher 服務資訊,version 是合并後最新的版本号。
服務上下線過程
服務的上下線過程是指服務通過代碼調用執行正常注冊(Publisher#register) 和下線(Publisher#unregister)操作,不考慮因為服務當機等意外情況導緻的下線場景。
“一次服務注冊過程”的服務資料在 SOFARegistry 内部流轉過程:
- 用戶端 Client 調用服務釋出者 Publisher 的 register 向 SessionServer 注冊服務。
- SessionServer 接收到服務資料即 PublisherRegister 寫入記憶體 (SessionServer 存儲 Client 的服務資料到記憶體,用于後續跟 DataServer 做定期檢查),接着根據 dataInfoId 的一緻性 Hash 尋找對應的 DataServer,将 PublisherRegister 發送給 DataServer。
- DataServer 接收到 PublisherRegister 資料首先也是把資料寫入記憶體 ,DataServer 以 dataInfoId 的次元彙總所有 PublisherRegister。同時 DataServer 将該 dataInfoId 的變更事件通知給所有 SessionServer,變更事件内容包括 dataInfoId 和版本号資訊 version 等。
- DataServer 同時異步以 dataInfoId 次元增量同步資料給其他副本,考慮到 DataServer 在一緻性 Hash 分片的基礎上對每個分片儲存多個副本(預設是3個副本)。
- SessionServer 接收到變更事件通知對比 SessionServer 記憶體中存儲的 dataInfoId 的版本号 version,若發現比 DataServer 發送的版本号小則主動向 DataServer 擷取 dataInfoId 的完整資料,即包含所有該 dataInfoId 具體的 PublisherRegister 服務清單。
- SessionServer 把資料推送給對應的用戶端 Client,Client 即接收到此次服務注冊之後最新服務清單資料。
“一次服務下線過程”的服務資料在 SOFARegistry 内部流轉過程:
- 用戶端 Client 調用服務釋出者 Publisher 的 unRegister 向 SessionServer 下線服務。
- SessionServer 擷取到服務資料 PublisherRegister 按照 Publisher 注冊 Id 删除記憶體服務資訊資料,然後根據 dataInfoId 的一緻性 Hash 尋找對應的 DataServer,将 PublisherRegister 發送給 DataServer。
- DataServer 擷取到 PublisherRegister 資料首先删除記憶體服務資訊資料,DataServer 以 dataInfoId 的次元彙總所有 PublisherRegister。同時 DataServer 将該 dataInfoId 的變更事件通知給所有 SessionServer,變更事件内容包括 dataInfoId 和版本号資訊 version 等。
- DataServer 同時異步以 dataInfoId 次元增量同步給其他副本資料。
- SessionServer 推送服務資料給對應的用戶端 Client,Client 即接收到這次服務下線之後最新服務清單資料。
“一次斷連下線過程”的服務資料在 SOFARegistry 内部流轉過程:
- 用戶端 Client 節點斷連下線調用連接配接處理器 fireCancelClient 觸發取消用戶端,通過連接配接用戶端線程池檢查連接配接緩存執行 SessionRegistry 取消用戶端連接配接,避免阻塞連接配接 ConnectionEventExecutor 線程池。
- SessionRegistry 通過 SessionDataStore 輪詢注冊中心服務釋出者 Publisher 删除本地存儲的連接配接,使用 SessionInterests 周遊服務訂閱者 Subscriber 移除指定連接配接消費者,調用 SessionWatchers 失效連接配接監聽者 Watcher。
- WriteDataAcceptor 負責處理 DataServer 寫操作包括 ClientOff,SessionRegistry 異步調用 WriteDataAcceptor 同步 DataServer 用戶端斷連資料。DataServer 建立寫資料處理器 WriteDataProcessor 将 ClientOff 寫請求放入隊列處理。
- WriteDataProcessor 根據寫請求類型為 CLIENT_OFF 調用 doClientOffAsync 發送 CANCEL_DATA_TASK 事件,CancelDataTaskListener 監聽取消資料任務事件建立 CancelDataTask 任務送出給 Dispatcher 攢批處理。SessionServer 通過 CancelDataTask 調用 clientOff 暫停用戶端節點并且移除所有 DataServer 已經注冊的資料,輪詢本地 DataCenter 資料節點建構用戶端下線請求送出給 DataNodeExchanger 擷取 DataServer 用戶端連接配接發起請求。
- DataServer 接收 ClientOff 請求采用 DatumLeaseManager 停止連接配接對應任務,送出 DataChangeEventQueue 隊列用戶端斷連事件。DataChangeEventQueue 啟動線程調用 handleClientOff 處理用戶端變更事件,通過 DatumCache 擷取指定連接配接的服務釋出者,輪詢 Publisher 建構資料變更類型為 MERGE 且資料來源類型為 PUB 的 UnPublisher Datum,更新 Publisher 和 Datum 緩存。
基于 SOFARegistry 上下線服務資料流轉流程,整理 SOFARegistry 内部角色之間的資料互動方式:
- SessionServer 和 DataServer 之間的通信采用基于推拉結合的機制
- 推:DataServer 在服務資料有變化時主動通知 SessionServer,SessionServer 檢查确認需要更新(對比版本号 version) 主動向 DataServer 擷取資料。
- 拉:除了上述的 DataServer 主動推以外,SessionServer 每隔一定的時間間隔(預設30秒)主動向 DataServer 查詢所有 dataInfoId 的 version 資訊,再對比 SessionServer 記憶體的版本号 version,若發現 version 有變化則主動向 DataServer 擷取資料。這個“拉”的邏輯,主要是對“推”的一個補充,若在“推”的過程有錯漏的情況可以在這個時候及時彌補。
- Client 與 SessionServer 之間的通信使用基于推的機制
- SessionServer 在接收到 DataServer 的資料變更推送,或者 SessionServer 定期查詢 DataServer 發現資料有變更并且重新擷取之後,直接将 dataInfoId 的資料推送給 Client。如果此過程由于網絡原因沒能成功推送給 Client,SessionServer 嘗試做指定次數(預設是5次)的重試,最終還是失敗的話依然會在 SessionServer 定期每隔 30s 輪訓 DataServer 時再次推送服務資料給 Client。
總結
本文圍繞服務的健康檢測,服務上下線過程以及基于 SOFARegistry 通知服務上下線方面闡述 SOFARegistry 實作秒級服務上下線通知基本原理,剖析服務的健康檢測通過連接配接敏感的特性對服務當機做到秒級發現,概括 SOFARegistry 内部角色之間的“推”和“拉”的機制,服務上下線流程以實時的“推”為主做到秒級通知機制。