分佈式架構之架構 2-0
一、架構 2.0 的定義
經過三到四個版本的迭代以後,將原有開源項目的一些我們用不到的組件進行去除,並同時將過去三層架構與微服務的這套體系進行合併與移除,合併通用的,移除棄用的。相當於變成這樣:
關於我是如何對系統重構的 (變三層爲一層),感興趣的朋友可以閱讀這篇文章:
談談系統重構
二、架構中的微服務去中心化治理
曾讀過一本書叫《分佈式服務架構: 原理、設計與實戰》,書中提到微服務的交互模式一共有 3 種,微服務的分解和組合模式一共有 6 種,微服務的容錯模式一共 4 種。我結合這本書上面的理論知識談談我在架構 2.0 的實踐。
1. 微服務的交互模式
(1) 讀者容錯模式
讀者容錯模式指微服務化中服務提供者和消費者之間如何對接口的改變進行容錯。從字面上來講,消費者需要對提供者提供的功能進行兼容性設計,尤其對服務提供者返回的內容進行兼容,或者解決在服務提供者改變接口或者數據的格式的情況下,如何讓服務消費者正常運行。
(2) 消費者驅動契約模式
消費者驅動契約模式用來定義服務化中服務之間交互接口改變的最佳規則。
服務契約分爲: 提供者契約、消費者契約以及消費者驅動的契約,它從期望與約束的角度描述了服務提供者與服務消費者之間的聯動關係。
-
a. 提供者契約: 是我們最常用的一種服務契約,顧名思義,提供者契約是以提供者爲中心的,提供者提供了什麼功能和消息格式,各消費者都會無條件地遵守這些約定,不論消費者實際需要多少功能,消費者接受了提供者契約時,都會根據服務提供者的規則來使用服務。
-
b. 消費者契約: 是對某個消費者的需求進行更爲精確的描述,在一次具體的服務交互場景下,代表消費者需要提供者提供功能中的哪部分數據。消費者契約可以被用來標識現有的提供者契約,也可以用來發現一個尚未明確的提供者契約。
-
c. 消費者驅動的契約: 代表服務提供者向其所有當前消費者承諾遵守的約束。一旦各消費者把自己的具體期望告知提供者,則提供者無論在什麼時間和場景下,都不應該打破契約。
(3) 去數據共享模式
與 SOA 服務化對比,微服務是去 ESB 總線、去中心化及分佈式的;而 SOA 還是以 ESB 爲核心實現遺留系統的集成,以及基於 Web Service 爲標準實現的通用的面向服務的架構。在微服務領域,微服務之間的交互通過定義良好的接口來實現,不允許使用共享數據來實現。
(4) 我的實踐
過去的三層架構和微服務體系,從 Web 接口服務、業務邏輯服務到數據存取服務,容錯方面基本上比較寬鬆,通用性錯誤歸納爲三類:
-
a. 參數錯誤;
-
b. 數據組裝或計算錯誤;
-
c.SQL 報錯。
參數錯誤一般發生在 Web 接口服務,數據組裝或計算錯誤一般發生在業務邏輯服務,SQL 報錯一般發生在數據存取服務。而 API 之間的調用的結果通常有且只有兩種 (參數錯誤排除在外),一個是響應碼爲 200,另外一個是響應碼爲 500。
針對響應碼進行數據提取,響應碼正常,數據提取正常,哪怕爲空也沒關係,消費者一方也能自行判斷),而響應碼非 200 的情況下,則說明出問題了,而通常這種錯誤,我們直接拋給調用者,調用者通常不會將數據返給前端,而是內部處理後,反饋一個 200 給前端展示,雖然容錯比較好,但不利於問題的發現,於是我們後來引入了枚舉,通過枚舉定義接口調用的異常處理,這有助於我們發現問題、定位問題、解決問題。這一點體現了讀者容錯模式,客觀地說並未做的那麼好。
服務之間的調用,哪怕取緩存裏的數據或者調用第三方數據庫及其其它數據庫中的數據,我們均定義接口,通過接口的方式來獲取,而不是每次一個接口服務將中間結果存到緩存或數據庫中,其它接口服務再從中拿出來繼續處理。這一點體現了去數據共享模式,我自認爲做的很不錯。因爲在微服務的劃分或者是代碼層的劃分,我非常贊同一點,一個接口 (細粒度) 或者一個微服務 (細粒度的集合體) 只做一件事情。
讀者容錯模式和去數據共享模式這一點都在架構 2.0 中體現着。
2. 微服務的分解和組合模式
(1) 服務代理模式
服務代理模式是最簡單的服務組合模式,它根據業務的需求選擇調用後端的某個服務。在返回給使用端之前,代理可以對後端服務的輸出進行加工,也可以直接把後端服務的返回結果返回給使用端。
(2) 服務聚合模式
服務聚合模式是最常用的服務組合模式,它根據業務流程處理的需要,以一定的順序調用依賴的多個微服務,對依賴的微服務返回的數據進行組合、加工和轉換,最後以一定的形式返回給使用方。
這裏,每個被依賴的微服務都有自己的緩存和數據庫,聚合服務本身可以有自己的數據存儲,包括緩存和數據庫等,也可以是簡單的聚合,不需要持久化任何數據。
(3) 服務串聯模式
服務串聯模式類似一個工作流,最前面的服務 1 負責接收請求和響應使用方,串聯服務後再與服務 1 交互,隨後服務 1 與服務 2 交互,最後,從服務 2 產生的結果經過服務 1 和串聯服務逐個處理後返回給使用方。
(4) 服務分支模式
服務分支模式是服務代理模式、服務聚合模式和服務串聯模式相結合的產物。
分支服務可以擁有自己的數據庫存儲,調用多個後端的服務或者服務串聯鏈,然後將結果進行組合處理再返回給客戶端。分支服務也可以使用代理模式,簡單地調用後端的某個服務或者服務鏈,然後將返回的數據直接返回給使用方。
(5) 服務異步消息模式
前面所提到的服務代理模式、服務聚合模式、服務串聯模式、服務分支模式都使用了同步的 Restful 風格的同步調用來實現,同步調用模式在調用的過程中會阻塞線程,如果服務提供方遲遲沒有返回,則服務消費方會一直阻塞,在嚴重情況下會撐滿服務的線程池,出現雪崩效應。
(6) 服務共享數據模式
服務共享數據模式其實是反模式,前面提出了去數據共享模式,由於去掉了數據共享,所以僅僅通過服務之間良好定義的接口進行交互和通信,使得每個服務都是自治的,服務本身和服務的團隊包含全角色棧的技術和運營人員,這些人都是專業的人做專業的事,使溝通在團隊內部解決,因此可以使效率最大化。
(7) 我的實踐
架構 2.0 體現了服務代理模式 (服務代理模式不論是常規的單體微服務還是通用型分佈式微服務均有體現)、服務串聯模式(前面提到過類似工作流,請求均爲同步請求,容易阻塞) 等。
而最早的架構 1.0 體現服務代理模式、服務聚合模式、服務串聯模式、服務分支模式等。
架構 2.0 最直觀概述,讓微服務的分解與組合變得更簡單,之前的三層架構和微服務體系 (架構 1.0) 相當於把流程複雜化而非簡單化,這次系統架構重構和升級爲 2.0,相當於將流程複雜化變爲簡單化。
3. 微服務的容錯模式
(1) 艙壁隔離模式
這裏用航船的設計比喻艙壁隔離模式,若一艘航船遇到了意外事故,其中一個船艙進了水,則我們希望這個船艙和其他船艙是隔離的,希望其他船艙可以不進水,不受影響。在微服務架構中,這主要體現如下兩個方面:
a. 微服務容器分組
將微服務的每個節點的服務池分爲三組: 準生產環境、灰度環境和生產環境。準生產環境供內測使用;灰度環境會跑一些普通商戶的流量;大部分生產環境和 VIP 商戶的流量則跑在生產環境中。
b. 線程池隔離
在微服務架構實施的過程中,我們不一定將每個服務拆分到微小的粒度,這取決於職能團隊和財務的狀況,我們一般會將同一類功能劃分在一個微服務中,儘量避免微服務過細而導致成本增加,適可而止。
這樣就會導致多個功能呢混合部署在一個微服務實例中,這些微服務的不同功能通常使用一個線程池,導致一個功能流量增加時耗盡線程池的線程,而阻塞其他功能的服務。
(2) 熔斷模式
可以用家裏的電路保險開關來比喻熔斷模式,如果家裏的用電量過大,則電路保險開關就會自動跳閘,這時需要人工找到用電量過大的電器來解決問題,然後打開電路保險開關。在這個過程中,電路保險開關起到保護整個家庭電路系統的作用。
對於微服務系統也一樣,當服務的輸入負載迅速增加時,如果沒有有效的措施對負載進行熔斷,則會使服務迅速被壓垮,服務被壓垮會導致依賴的服務都被壓垮,出現雪崩效應,因此,可通過模擬家庭的電路保險開關,在微服務架構中實現熔斷模式。
(3) 限流模式
針對服務突然上量,我們必須有限流機制,限流機制一般會控制訪問的併發量,例如每秒允許處理的併發用戶數及查詢量、請求量等。
(4) 失效轉移模式
若微服務架構中發生了熔斷和限流,則該如何處理被拒絕的請求呢?解決這個問題的模式叫做失效轉移模式,通常分爲下面幾種。
-
a. 採用快速失敗的策略,直接返回使用方錯誤,讓使用方知道發生了問題並自行決定後續處理。
-
b. 是否有備份服務,如果有備份服務,則迅速切換到備份服務。
-
c. 失敗的服務有可能是某臺機器有問題,而不是所有機器有問題,例如 OOM 問題,在這種情況下適合使用 failover 策略,採用重試的方法來解決,但是這種方法要求服務提供者的服務實現了冪等性。
(5) 我的實踐
微服務的容錯主要採用熔斷模式、限流模式、失效轉移模式 (快速失敗策略) 等。
熔斷模式的實現主要是 Hystrix,限流模式的實現集成了 Sentinel,失效轉移模式 (快速失敗策略) 全局異常處理機制已經處理好了。
容錯的目的在於整個系統不會因爲某個局部報錯或大量併發而導致整體不可用 (單體應用就存在這個問題)。
youcongtech 過去做運維,後來做全棧,如今做架構與技術管理。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/TkVjEBdW5HQPmFAXlQWXAg