一文理解分布式開發中的服務治理
我們在分布式開發中經常聽到的一個詞就是“服務治理”。在理解“服務治理”的概念之前讓我們先理解什么是分布式系統,分布式系統之間如何通過RPC(Remote Procedure Call,遠程過程調用)方式通信,以及如何解決RPC框架存在的問題,這樣才能真正地理解服務治理的核心思想。
分布式系統
分布式系統指的是通過網絡連接讓多臺計算機協同解決單臺計算機所不能解決的計算、存儲等問題,多臺計算機之間通過 RPC 方式通信。在使用分布式系統前,首要解決的問題是如何拆解當前面臨的問題。通過使用多臺計算機分布式解決問題,讓分布式系統中的每臺機器都負責解決原問題的一個子集。一般來說,可以使用橫向拆分法或者縱向拆分法對復雜的系統進行拆分。
橫向拆分:在無狀態系統中多部署幾個實例,通過負載均衡方式協調每個實例所負載的計算量。
縱向拆分:將一個大應用拆分為多個小應用(例如,將系統拆分為用戶、商品、訂單服務),每個小應用都負責處理一部分業務。
然而,雖然通過拆分法解決了計算或存儲的問題,但是使用分布式技術進行開發會引發比單體應用更多的問題,比如網絡異常、數據一致性及分布式系統性能等。因此,在使用分布式架構開發系統前,需要先深入理解分布式系統的概念和可能存在的異常。
1、分布式系統中的常見異常
服務器宕機:服務器宕機是分布式架構下最常見的異常之一。任何服務器都有可能發生故障,而且故障發生的類型、時間都不盡相同。所以,分布式系統一般允許部分服務器發生故障,但要求在部分服務器發生故障時不影響整個系統的正常使用。
網絡異常:服務器與服務器之間通過網絡通信,若在通信過程中出現消息丟失,則兩個節點之間無法進行通信,會出現網絡分化、消息亂序等網絡問題。
分布式系統的三態:如果某個節點向另一個節點發起RPC請求,比如節點A向節點B發送一個消息,節點B根據收到的消息完成某些操作,并將操作的結果通過消息返回給節點A,那么這個RPC請求的執行結果可能有三種狀態:成功、失敗、超時(未知)。我們將這三種狀態稱為分布式系統的三態。在設計架構時需要考慮成功、失敗、超時(未知)這三種狀態的處理方式。
存儲的數據丟失:對于有狀態節點來說,數據丟失意味著狀態丟失。通常只能從其他節點讀取、恢復該存儲數據的狀態。
2、分布式系統的副本分類
分布式系統的副本指的是在分布式系統中為數據或服務提供的冗余。該副本可分為服務副本和數據副本兩種類型。
服務副本:多個節點提供某種相同的服務,這種服務不依賴本地節點的存儲狀態,是一種無狀態服務。
數據副本:在不同的節點上持久化同一份數據。當出現某一個節點存儲的數據丟失時,可以從其他副本上讀取該數據。數據多副本是分布式系統解決數據丟失異常的唯一方法,因為數據被分散或者復制到不同的機器上,所以如何保證各臺主機之間數據的一致性,成為一個難點。
對于分布式系統而言,服務副本非常容易控制,由于服務本身具備無狀況特性,運維人員可以動態增加或者減少服務副本的數量,而不會影響服務接口返回數據的正確性。數據副本分布在不同的計算機上,從技術角度來看,數據的一致性面臨著巨大的挑戰。數據副本的一致性通常具有以下幾種情況。
強一致性:任何時刻任何用戶或節點都可以讀到最近一次成功更新的副本數據。這是程度最高的一致性要求,也是實踐中最難實現的一致性。
弱一致性:系統并不保證進程或者線程在任何時刻訪問數據都會返回最新的更新過的值。系統在數據成功寫入之后,不承諾立即讀到最新寫入的值,也不承諾最終多久之后可以讀到最新值。
最終一致性:數據一旦更新成功,各個副本上的數據最終將達到完全一致的狀態,但需要一定的時間。
然而,分布式系統也存在一些復雜特性,比如分布式系統的三態性、異構性、透明性、并發性、可擴展性等。我們在應用分布式系統的過程中要仔細斟酌這些特性的優勢和副作用。
3.分布式系統的設計原則
異構性:由于分布式系統基于不同的網絡、操作系統、計算機硬件和編程語言,因此必須考慮采用一種通用的網絡通信協議來屏蔽異構系統之間的差異。開發人員一般選擇中間件來屏蔽這些差異。
透明性:分布式系統中任意組件的故障及主機的升級或遷移,對用戶來說都是透明的。
并發性:應用分布式系統的目的是更好地共享資源,所以系統中的每個資源在并發環境下都必須是安全的。
可擴展性:隨著業務量的增加,系統必須具備可擴展性,以應對因業務量增長而增加的外部流量。
故障獨立性:任何計算機都有可能發生故障,而且各計算機發生的故障類型不盡相同,發生故障的時間也各不相同。所以,分布式系統一般允許發生部分故障,而不影響整個系統的正常使用。
數據一致性:因為數據被分散或者復制到不同的機器上,所以需要保證各臺服務器之間數據的一致性。
負載均衡:由于分布式系統是多機協同工作的系統,因此為了提高系統的整體效率和吞吐量,必須考慮最大化地發揮每個節點的作用,以最大化地利用資源,避免某個節點過載或者浪費資源。
4.分布式系統的衡量指標
系統的性能:系統每秒的事務處理能力,通常用TPS(Transactions Per Second)來衡量。
系統的可用性:系統在面對各種異常時可以正確提供服務的能力。該指標可以用系統停服的時間與正常服務時間的比例來衡量,也可以用某功能的失敗次數與成功次數的比例來衡量。系統的可用性是分布式系統的重要指標,是系統容錯能力的體現。
系統的可擴展性:分布式系統通過擴展集群的機器規模來提高系統性能(增大接口吞吐量、降低接口延時、增大接口并發量)、存儲容量、計算能力的特性。
RPC框架
RPC(Remote Procedure Call,遠程過程調用)是一種進程間通信方式,也是一種技術思想。使用 RPC 技術時,允許本地程序通過網絡調用另一臺服務器上的函數或者方法,具體調用過程一般由 RPC 框架實現,不用編碼實現。即無論是調用本地函數還是調用遠程函數,我們編寫的調用代碼在本質上基本相同。
1.RPC框架的工作原理
RPC框架要向服務調用方和服務提供方屏蔽各類復雜性操作,比如負載均衡、序列化和反序列化、網絡重試、超時等,主要由客戶端、服務器端和注冊中心3種角色構成,整體架構如圖3-1所示。
客戶端(Client):調用遠程服務的服務消費方。客戶端調用遠程服務就像調用本地函數一樣,客戶端負責序列化、反序列化、連接池管理、負載均衡、故障轉移、超時管理、異步管理等。
服務器端(Server):暴露服務的服務提供方。服務器端如同實現一個本地函數一樣來實現遠程服務提供,服務器端需要做收發包隊列、I/O線程、工作線程、序列化及反序列化等工作。
注冊中心:服務注冊與發現的注冊中心。
2.RPC調用說明
一次RPC調用流程主要由5部分組成,分別是客戶端、客戶端存根、服務器端存根、服務提供端和網絡傳輸,其調用流程如圖3-2所示。
客戶端:服務調用方。
客戶端存根:用于存放服務器端的地址信息,將客戶端的請求參數等信息打包成網絡消息,再通過網絡傳輸發送給服務器端。
服務器端存根:接收客戶端發送過來的請求消息并解包,然后調用本地服務處理。
服務提供端:服務的真正提供者。
網絡傳輸:底層數據傳輸,可以是TCP或HTTP。
服務治理
業務在剛開始時都是單體應用,隨著用戶量和訪問量的增加,在架構層面會發生變化,逐步由單體應用開發轉為分布式應用開發,比如把單體應用中的每個模塊都按照特定的方法拆分成一組獨立的服務,服務與服務之間通過HTTP或者RPC方式調用。隨著業務量的逐步增加,服務的數量也逐步增加。這時維護服務的URL地址就變得非常麻煩,所以需要設計一套系統來統一管理每個服務所對應的URL地址。這套系統就叫作注冊中心。當有多個服務時,消費者需要根據規則來調用相關服務,實現軟負載均衡,以達到資源利用率最大化的目的。因此,服務注冊、服務發現、負載均衡、流量削峰、版本兼容、服務熔斷、服務降級、服務限流等方面的問題,都是因服務拆分所引發的一系列問題。如何解決這些問題,讓服務更穩定地運行,就叫作服務治理。
總體來說,服務治理指的是企業為了確保事情順利完成而實施的內容,包括最佳實踐、架構原則、治理規程、規律及其他決定性的因素。下面針對服務治理過程中的各個環節做相關說明。
(1)服務:它是分布式架構下的基礎單元,包括一個或一組軟件功能,其目的是不同的客戶端通過網絡獲取相應的數據,而不用關注底層實現的具體細節。以用戶服務為例,當客戶端調用用戶服務的注冊功能時,注冊信息會被寫入數據庫、緩存并發送消息來通知其他關注注冊事件的系統,但是調用方并不清楚服務的具體處理邏輯。
(2)注冊中心:它是微服務架構中的“通訊錄”,記錄了服務和服務地址的映射關系,主要涉及服務的提供者、服務注冊中心和服務的消費者。在數據流程中,服務提供者在啟動服務之后將服務注冊到注冊中心;服務消費者(或稱為服務消費方)在啟動時,會從注冊中心拉取相關配置,并將其放到緩存中。注冊中心的優勢在于解耦了服務提供者和服務消費者之間的關系,并且支持彈性擴容和縮容。當服務需要擴容時,只需要再部署一個該服務。當服務成功啟動后,會自動被注冊到注冊中心,并推送給消費者。
(3)服務注冊與發布:服務實例在啟動時被加載到容器中,并將服務自身的相關信息,比如接口名稱、接口版本、IP地址、端口等注冊到注冊中心,并使用心跳機制定期刷新當前服務在注冊中心的狀態,以確認服務狀態正常,在服務終止時將其從注冊表中刪除。服務注冊包括自注冊模式和第三方注冊模式這兩種模式。
◎自注冊模式:服務實例負責在服務注冊表中注冊和注銷服務實例,同時服務實例要發送心跳來保證注冊信息不過期。其優點是,相對簡單,無須其他系統功能的支持;缺點是,需要把服務實例和服務注冊表聯系起來,必須在每種編程語言和框架內部實現注冊代碼。
◎第三方注冊模式:服務實例由另一個類似的服務管理器負責注冊,服務管理器通過查詢部署環境或訂閱事件來跟蹤運行服務的改變。當管理器發現一個新的可用服務時,會向注冊表注冊此服務,同時服務管理器負責注銷終止的服務實例。第三方注冊模式的主要優勢是服務與服務注冊表是分離的,無須為每種編程語言和架構都完成服務注冊邏輯。相應地,服務實例是通過一個集中化管理的服務進行管理的;缺點是,需要一個高可用系統來支撐。
(4)服務發現:使用一個注冊中心來記錄分布式系統中全部服務的信息,以便其他服務快速找到這些已注冊的服務。其目前有客戶端發現模式和服務器端發現模式這兩種模式。
客戶端發現模式:客戶端從服務注冊服務中查詢所有可用服務實例的地址,使用負載均衡算法從多個服務實例中選擇一個,然后發出請求。其優勢在于客戶端知道可用服務注冊表的信息,因此可以定義多種負載均衡算法,而且負載均衡的壓力都集中在客戶端。
服務器端發現模式:客戶端通過負載均衡器向某個服務提出請求,負載均衡器從服務注冊服務中查詢所有可用服務實例的地址,將每個請求都轉發到可用的服務實例中。與客戶端發現一樣,服務實例在服務注冊表中注冊或者注銷。我們可以將HTTP服務、Nginx的負載均衡器都理解為服務器端發現模式。其優點是,客戶端無須關注發現的細節,可以減少客戶端框架需要完成的服務發現邏輯;客戶端只需簡單地向負載均衡器發送請求。其缺點是,在服務器端需要配置一個高可用的負載均衡器。
(5)流量削峰:使用一些技術手段來削弱瞬時的請求高峰,讓系統吞吐量在高峰請求下可控,也可用于消除毛刺,使服務器資源的利用更加均衡、充分。常見的削峰策略有隊列、限頻、分層過濾、多級緩存等。
(6)版本兼容:在升級版本的過程中,需要考慮升級版本后新的數據結構能否理解和解析舊的數據,新協議能否理解舊的協議并做出預期內合適的處理。這就需要在服務設計過程中做好版本兼容工作。
(7)服務熔斷:其作用類似于家用的保險絲。當某服務出現不可用或響應超時的情況時,已經達到系統設定的閾值,為了防止整個系統出現雪崩,會暫時停止對該服務的調用。
(8)服務降級:在服務器壓力劇增的情況下,根據當前業務情況及流量對一些服務和頁面有策略性地降級,以此釋放服務器資源,保證核心任務的正常運行。降級時往往會指定不同的級別,面對不同的異常等級執行不同的處理。
(9)服務限流:服務限流可以被認為是服務降級的一種。它通過限制系統的輸入和輸出流量來達到保護系統的目的。一般來說,系統的吞吐量是可以被測算的。為了保證系統的穩定運行,一旦達到閾值,就需要限制流量。限制措施有延遲處理、拒絕處理或者部分拒絕處理等。
(10)負載均衡策略:它是用于解決一臺機器無法處理所有請求而產生的一種算法。當集群里的1臺或者多臺服務器不能響應請求時,負載均衡策略會通過合理分攤流量,讓更多的服務器均衡處理流量請求,不會因某一高峰時刻流量大而導致單個服務器的 CPU或內存急劇上升。