讀構建可擴充套件分散式系統:方法與實踐08微服務

躺柒發表於2024-09-19

1. 微服務

1.1. 微服務的起源可以追溯到2008年左右

  • 1.1.1. 在Amazon,​“兩個比薩原則”成為一個單系統元件團隊規模的管理原則,後來被稱為微服務

    • 1.1.1.1. 每個內部團隊都應該小到可以用兩個比薩餅餵飽
  • 1.1.2. Amazon和Netflix是微服務架構的先驅,他們在2009年左右大規模採用了微服務架構

  • 1.1.3. Uber已將其微服務架構發展為基於相關服務的集合(稱為域)

1.2. 認為微服務在某種意義上比服務更小,是對微服務的一種誤解

1.3. 微服務是細粒度、高內聚、松耦合服務的一種設計和部署方法,服務被組合起來滿足系統的需求

  • 1.3.1. 定義特徵是它們的範圍,圍繞業務能力組織

  • 1.3.2. 細粒度服務或微服務是獨立部署的,並且必須在必要時相互通訊和協調以處理各個系統請求

  • 1.3.3. 微服務架構是分散式系統

1.4. 微服務是一種流行的現代架構風格,在合適的場景中具有大量工程優勢

  • 1.4.1. 每個微服務對系統其他部分來說都是黑盒子,它可以內部選擇最適合團隊和應用程式需求的架構和技術棧

  • 1.4.2. 主要的系統新功能可以構建為微服務並組合到應用程式架構中,同時最小化對系統其餘部分的影響

1.5. 有時微服務並不總是正確的方法

1.6. 採用微服務需要引入新的設計和開發實踐,以建立一組細粒度、內聚的元件來滿足你的應用程式需求

1.7. 微服務之間經常協作,需要進行通訊來滿足單個請求

  • 1.7.1. 緩慢的響應會導致呼叫服務中的背壓,最終一個故障會導致所有相關服務崩潰

1.8. 微服務要求開發過程廣泛自動化

  • 1.8.1. DevOps是一個發展快速且技術豐富的領域,搜尋引擎是查詢構成現代化DevOps管道的各種構建、配置、測試、部署和監控平臺資訊的最佳途徑

2. 轉向微服務

2.1. 基於微服務的架構在很多方面受益於過去10年出現的軟體工程和技術創新的融合

2.2. 單體應用

  • 2.2.1. 自IT系統問世以來,單體架構風格一直主導著企業應用程式

  • 2.2.2. 將應用程式分解為多個邏輯模組或服務,它們作為單個應用程式構建和部署

  • 2.2.3. 單體架構鼓勵建立可在服務實現之間共享的可重用業務邏輯和DAO

    • 2.2.3.1. DAO對映到資料庫實體,所有服務實現共享一個資料庫
  • 2.2.4. IBM WebSphere和Microsoft.NET等主流平臺都支援將所有服務構建和部署為單個可執行包,即術語“單體”​(完整的應用程式)的來源

    • 2.2.4.1. API、業務邏輯、資料訪問等都包含在一個部署件中
  • 2.2.5. 由於應用程式在一臺(可能非常強大的)伺服器上執行,因此係統和錯誤監控也得到了簡化

  • 2.2.6. 垂直擴充套件(向上擴充套件)是提高單體應用響應能力和容量的最簡單方法

  • 2.2.7. 水平擴充套件(向外擴充套件)也是可行的

    • 2.2.7.1. 可以提供兩個或多個單體副本,並使用負載均衡器來分發請求

    • 2.2.7.2. 有狀態和無狀態服務都適用,只要負載均衡器支援有狀態設計的會話保持特性

  • 2.2.8. 問題

    • 2.2.8.1. 程式碼庫的複雜度

      2.2.8.1.1. 隨著應用程式和工程團隊規模的增長,新增新功能、測試和重構變得越來越困難

      2.2.8.1.2. 技術債務不可避免地會累積

      2.2.8.1.3. 如果沒有持續一致的重構努力來維持架構的完整性和程式碼質量,工程就會變得更加困難

    • 2.2.8.2. 水平擴充套件

      2.2.8.2.1. 你可以透過在多個節點上覆制應用程式來水平擴充套件以增加容量

      2.2.8.2.1.1. 每次都要複製整個應用程式(單體)

      2.2.8.2.2. 隨著請求量的快速增長,它們提供了方案來解決擴充套件單體工程幾乎不可避免地面臨的挑戰

2.3. 打破巨型單體

  • 2.3.1. 微服務架構將應用程式功能分解為多個獨立的服務,這些服務在必要時進行通訊和協調

  • 2.3.2. 每個微服務都是完全獨立的,在需要的地方封裝自己的資料儲存,並提供用於通訊的API

  • 2.3.3. 優勢

    • 2.3.3.1. 程式碼庫

      2.3.3.1.1. 遵循兩個比薩原則,單個服務的複雜程度不應超過小型團隊可以構建、發展和管理的程度

      2.3.3.1.2. 團隊可以完全自主地選擇自己的開發棧和資料管理平臺

      2.3.3.1.3. 一個設計良好的微服務支援的功能範圍更窄、更加內聚,這會降低程式碼的複雜度,加快新功能的開發節奏

      2.3.3.1.4. 按需獨立部署微服務的修訂版

      2.3.3.1.5. 如果微服務支援的API是穩定的,則更改對依賴的服務是透明的

    • 2.3.3.2. 水平擴充套件

      2.3.3.2.1. 可以透過擴充套件單個微服務來滿足請求量和延遲要求

      2.3.3.2.2. 其他輕負載的服務可以簡單地在單個節點上執行或以低成本進行復制,以消除單點故障並提高可用性

      2.3.3.2.3. 如何將系統功能分解為單獨的服務,是轉向微服務架構的關鍵設計決策之一

      2.3.3.2.3.1. DDD(Domain-Driven Design,領域驅動設計)提供了一種合適的方法來識別微服務

      2.3.3.2.3.2. 微服務必須自包含的性質可以很好地對映到DDD中有界上下文的概念

  • 2.3.4. 微服務本質上是分散式的,總有一個平衡的考量

    • 2.3.4.1. 需要分析和調整領域模型的純度,以滿足分散式通訊的成本以及系統管理和監控的複雜性

    • 2.3.4.2. 在耦合的微服務之間複製資料

      2.3.4.2.1. 服務能夠在本地訪問它需要的資料,從而簡化設計並減少資料訪問響應時間

      2.3.4.2.2. 複製資料也需要權衡其利弊

      2.3.4.2.2.1. 需要額外的儲存容量和開發工作來確保所有重複資料的一致性狀態

      2.3.4.2.2.2. 當資料發生變化時,可以立即啟動資料副本的更新,以儘量減少重複資料不一致的時間間隔

      2.3.4.2.2.3. 如果業務上下文允許,可以執行定期複製(例如,每小時或每天)​,可能是在請求負載較低時呼叫計劃任務執行

      2.3.4.2.2.4. 隨著應用程式對效能和可擴充套件性的需求不斷增長,與系統重大重構所帶來的問題相比,複製資料的成本和複雜性通常很小

  • 2.3.5. 部署微服務

    • 2.3.5.1. 為了支援頻繁更新並從小型團隊提供的靈活性中獲益,你需要能夠簡單快速地部署新的微服務版本

    • 2.3.5.2. 無伺服器處理平臺是一種有吸引力的微服務部署方法

      2.3.5.2.1. 微服務可以在你選擇的無伺服器平臺上構建,並公開API

    • 2.3.5.3. 無伺服器平臺的優點

      2.3.5.3.1. 部署簡單

      2.3.5.3.1.1. 只需將微服務的新可執行包上傳到為函式配置的端點

      2.3.5.3.2. 按使用量付款

      2.3.5.3.2.1. 如果你的服務只有少量請求,那麼你的成本會很低,甚至為零

      2.3.5.3.3. 易於擴充套件

      2.3.5.3.3.1. 你選擇的平臺會處理函式的擴充套件

    • 2.3.5.4. 當你將所有微服務都部署到無伺服器平臺上時,你將為客戶端暴露多個需要被呼叫的端點

      2.3.5.4.1. 將後端更改直接暴露給客戶端從來不是一個好主意

    • 2.3.5.5. API閘道器模式

      2.3.5.5.1. 將客戶端與實現應用功能的微服務的底層架構隔離開來

      2.3.5.5.2. NGINX Plus

      2.3.5.5.3. Kong

      2.3.5.5.3.1. Kong API閘道器是無狀態的,因此可以部署多個例項並使用負載均衡器分發請求

      2.3.5.5.4. 特定雲供應商的託管產品

      2.3.5.5.5. 以毫秒級低延遲將傳入的客戶端API請求代理到實現API的後端微服務

      2.3.5.5.5.1. 由API閘道器處理的面向客戶端的API與後端微服務API之間的對映是透過管理工具或配置檔案執行的

  • 2.3.6. 微服務原則

    • 2.3.6.1. 圍繞業務領域進行建模

      2.3.6.1.1. 有界上下文的概念為微服務的範圍提供了一個起點

      2.3.6.1.2. 在微服務之間存在耦合並且可能引入效能開銷的情況下,可能需要重新考慮業務領域邊界

    • 2.3.6.2. 高度可觀測

      2.3.6.2.1. 監控每項服務對於確保它們按預期執行、以低延遲處理請求以及記錄錯誤情況至關重要

      2.3.6.2.2. 在分散式系統中,可觀測性是高效執行的基本特徵

    • 2.3.6.3. 隱藏實現細節

      2.3.6.3.1. 微服務是黑盒子

      2.3.6.3.2. 它們的API是其保證支援的契約,但是API內部如何執行不會對外公開

    • 2.3.6.4. 全面去中心化

      2.3.6.4.1. 處理需要多次呼叫下游微服務的客戶端請求,它們通常被稱為工作流

    • 2.3.6.5. 隔離故障

      2.3.6.5.1. 一個微服務的故障不應傳播到其他微服務並導致應用程式崩潰

    • 2.3.6.6. 獨立部署

      2.3.6.6.1. 每個微服務都應該是可獨立部署的,以便團隊能夠在不依賴於其他團隊進度的情況下進行增強和修改

    • 2.3.6.7. 自動化文化

      2.3.6.7.1. 想要從微服務獲得好處,開發和DevOps的工具及實踐是絕對必要的

      2.3.6.7.2. 自動化使得修改已部署的系統變得更快、更可靠

    • 2.3.6.8. 工作流

      2.3.6.8.1. 編制和編排通常用於實現需要訪問多個微服務的用例

    • 2.3.6.9. 點對點編排

      2.3.6.9.1. 所需的微服務之間直接通訊即可滿足請求

      2.3.6.9.2. 每個自治微服務共享處理工作流的責任和知識

      2.3.6.9.3. 通訊可以是同步的,也可以是非同步的

    • 2.3.6.10. ⑩集中編制

      2.3.6.10.1. 實現工作流的邏輯被嵌入單個元件中,通常是專用的微服務

      2.3.6.10.2. 該服務與域服務通訊並將結果傳送回使用者

      2.3.6.10.3. 兩種方法都需要權衡取捨

3. 微服務的彈性

3.1. 分散式系統的一個老生常談的話題是,在絕大多數時間裡,系統執行不會出現災難性錯誤

  • 3.1.1. 受益於快速可靠的網路、很少崩潰的機器和磁碟、用於託管微服務及進行訊息傳遞的基礎平臺和資料庫非常強大

  • 3.1.2. 你的系統仍然必須為可能發生的間歇性故障做好準備,通常是在最不方便的時候

3.2. 級聯故障

  • 3.2.1. TCP請求將超時並向呼叫方丟擲錯誤

  • 3.2.2. 級聯故障的潛在本質是,它們是由所依賴的服務響應緩慢觸發的

  • 3.2.3. 如果下游服務只是由於系統崩潰或暫時性網路故障而失敗或不可用,呼叫者會立即收到錯誤並可以做出相應的響應

  • 3.2.4. 對於逐漸變慢的服務,情況並非如此

    • 3.2.4.1. 請求返回結果,只是響應時間更長

    • 3.2.4.2. 如果不堪重負的元件繼續受到請求的轟炸,它就沒時間恢復並且響應時間繼續增長

    • 3.2.4.3. 這種情況通常會因客戶端在請求失敗時立即重試而加劇

      3.2.4.3.1. 立即重試在不堪重負的微服務上維持負載,帶來的結果是可預測的,即另一個異常

      3.2.4.3.2. 重試只是保持壓力

      3.2.4.3.3. 在兩次重試之間插入不斷增加的延遲

      3.2.4.3.3.1. 有助於減輕下游的負載,但這個延遲會計入呼叫者所經歷的延遲,因此通常無法解決問題

  • 3.2.5. 級聯故障在分散式系統中很常見

    • 3.2.5.1. 無論是由不堪重負的服務引起,還是由錯誤情況(例如應用程式程式碼錯誤或網路問題)引起,你都需要採取明確的步驟來防範它們
  • 3.2.6. 避免級聯故障的模式包括使用超時和斷路器發起快速失敗

  • 3.2.7. 快速失敗模式

    • 3.2.7.1. 服務慢的核心問題是它們會長時間佔用系統資源來處理請求

    • 3.2.7.2. 請求執行緒會暫停,直至它收到響應

    • 3.2.7.3. 少量請求花費的時間比平均響應時間長得多

      3.2.7.3.1. 使用百分位數來量化慢速請求的百分比

      3.2.7.3.2. 百分位數提供了比平均值更豐富、更準確的微服務響應時間檢視

    • 3.2.7.4. 伺服器的垃圾回收、資料庫爭用、過多的上下文切換、系統頁面錯誤和網路請求丟失都是造成這種長尾的常見原因

    • 3.2.7.5. 長響應時間從來都不是好事,無論是技術上還是使用者感受上

      3.2.7.5.1. 消除長響應時間的常見方法是快速失敗

      3.2.7.5.1.1. 當請求花費的時間超過某個預設的時間限制時,客戶端不會等待它完成,而是會向其呼叫者返回一個錯誤

      3.2.7.5.1.2. 在伺服器上啟用節流

3.2.7.5.1.2.1. 如果請求負載超過某個閾值,則立即使請求失敗並返回HTTP503錯誤

3.2.7.5.1.2.1.1. 這向客戶端表明該服務不可用

  • 3.2.7.6. 節流或限制速率是許多負載均衡器和API閘道器技術中可用的功能

    3.2.7.6.1. 當達到定義的限制時,負載均衡器將拒絕請求,保護它控制的資源免於過載

    3.2.7.6.1.1. 這使服務能夠以一致的低響應時間處理請求

    3.2.7.6.2. 一種稍微複雜的方法可以使用滑動視窗演算法跟蹤平均響應時間或P99等指標

  • 3.2.7.7. 請求失敗時還需要考慮一件事

    3.2.7.7.1. 微服務的一個原則是故障隔離

    3.2.7.7.2. 這意味著部分系統的故障不會導致整個應用程式不可用

  • 3.2.8. 斷路器模式

    • 3.2.8.1. 如果微服務由於過載或網路不穩定而開始丟擲錯誤,那繼續向API傳送請求是毫無意義的

    • 3.2.8.2. 可以使用斷路器模式來實現這個功能,它可以保護遠端端點在某些錯誤情況發生時不會不堪重負

      3.2.8.2.1. 客戶端可以使用斷路器來保護伺服器免於過載

      3.2.8.2.2. 斷路器配置為監視某些條件,例如端點的錯誤響應率或每秒傳送的請求數

    • 3.2.8.3. 對於幾乎肯定會失敗的操作,斷路器是減少資源使用率的重要方式

    • 3.2.8.4. 當服務(有望)恢復時,斷路器會自動重置並恢復正常操作

    • 3.2.8.5. 斷路器對於故障隔離非常有效

      3.2.8.5.1. 它們保護客戶端免受依賴服務的錯誤操作影響,並允許服務恢復

      3.2.8.5.2. 在讀取密集型場景中,當斷路器開啟時,請求通常可以返回預設或快取的結果

      3.2.8.5.2.1. 有效地向客戶端隱藏了故障,並且不會降低服務吞吐量和響應時間

    • 3.2.8.6. 請確保已將斷路器觸發器繫結到監控和日誌記錄基礎架構中,以便診斷故障原因

3.3. 艙壁模式

  • 3.3.1. 艙壁(bulkhead)一詞的靈感來自大型造船實踐

  • 3.3.2. 船體內部被分成幾個物理分割槽,確保在船體的一部分發生洩漏時只有一個分割槽被淹沒,船會繼續漂浮,這是相當重要的

  • 3.3.3. 艙壁是一種損害限制策略

    • 3.3.3.1. 微服務中預留一定數量的執行緒來處理特定的請求

    • 3.3.3.2. 艙壁模式將遠端資源呼叫隔離在它們自己的執行緒池中,故單個過載或失敗的服務不會消耗應用伺服器中可用的所有執行緒

  • 3.3.4. 可用於確保在對微服務中某個API的請求激增期間,不會耗盡所有可用的資源

  • 3.3.5. 透過設定應用伺服器中特定API可以要求的最大執行緒數量限制,可以保證其他API的處理能力

相關文章