# 微服務架構最佳實踐 * * * #### 方法 ##### 服務粒度 針對微服務拆分過細導致的問題,建議基於團隊規模進行拆分,類似貝索斯在定義團隊規模時提出的「兩個披薩」理論(每個團隊的人數不能多到兩個披薩都不夠吃的地步)。 微服務拆分粒度的可參考「三個火槍手」原則[1][2],即一個微服務三個人負責開發。在實施微服務架構時,根據團隊規模來劃分微服務數量,如果業務規繼續發展,團隊規模擴大,再將已有的微服務進行拆分。例如,團隊最初有 6 個人,那麼可以劃分為 2 個微服務,隨著業務的發展,業務功能越來越多,邏輯越來越複雜,團隊擴展到 12 個人,那麼我們可以將已有的 2 個微服務進行拆分,變成 4 個微服務。 `為什麼是 3 個人,不是 4 個,也不是 2 個呢? ` > >**首先**,從系統規模來講,3 個人負責開發一個系統,系統的複雜度剛好達到每個人都能全面理解整個系統,又能夠進行分工的粒度;如果是 2 個人開發一個系統,系統的複雜度不夠,開發人員可能覺得無法體現自己的技術實力;如果是 4 個甚至更多人開發一個系統,系統複雜度又會無法讓開發人員對系統的細節都瞭解很深。 >**其次**,從團隊管理來說,3 個人可以形成一個穩定的備份,即使 1 個人休假或者調配到其他系統,剩餘 2 個人還可以支撐;如果是 2 個人,抽調 1 個後剩餘的 1 個人壓力很大;如果是 1 個人,這就是單點了,團隊沒有備份,某些情況下是很危險的,假如這個人休假了,系統出問題了怎麼辦? >**最後**,從技術提升的角度來講,3 個人的技術小組既能夠形成有效的討論,又能夠快速達成一致意見;如果是 2 個人,可能會出現互相堅持自己的意見,或者 2 個人經驗都不足導致設計缺陷;如果是 1 個人,由於沒有人跟他進行技術討論,很可能陷入思維盲區導致重大問題;如果是 4 個人或者更多,可能有的參與的人員並沒有認真參與,只是完成任務而已。 「三個火槍手」的原則主要應用於微服務設計和開發階段,如果微服務經過一段時間發展後已經比較穩定,處於維護期了,無須太多的開發,那麼平均 1 個人維護 1 個微服務甚至幾個微服務都可以。當然考慮到人員備份問題,每個微服務最好都安排 2 個人維護,每個人都可以維護多個微服務。 ##### 拆分方法 基於「三個火槍手」的理論,可以計算出拆分後合適的服務數量,但具體怎麼拆也是有技巧的,並不是快刀斬亂麻隨便拆分成指定數量的微服務就可以了,也不是只能按照業務來進行拆分,而是可以根據目的的不同靈活地選取不同的拆分方式。常見的拆分方式: 1. 基於業務邏輯拆分 這是最常見的一種拆分方式,將系統中的業務模塊按照職責範圍識別出來,每個單獨的業務模塊拆分為一個獨立的服務。 基於業務邏輯拆分雖然看起來很直觀,但在實踐過程中最常見的一個問題就是團隊成員對於「職責範圍」的理解差異很大,經常會出現爭論,難以達成一致意見。例如:假設我們做一個電商系統,第一種方式是將服務劃分為「商品」「交易」「用戶」3 個服務,第二種方式是劃分為「商品」「訂單」「支付」「發貨」「買家」「賣家」6 個服務,哪種方式更合理,是不是劃分越細越正確? 導致這種困惑的主要根因在於從業務的角度來拆分的話,規模粗和規模細都沒有問題,因為拆分基礎都是業務邏輯,要判斷拆分粒度,不能從業務邏輯角度,而要根據前面介紹的「三個火槍手」的原則,計算一下大概的服務數量範圍,然後再確定合適的「職責範圍」,否則就可能出現劃分過粗或者過細的情況,而且大部分情況下會出現過細的情況。 例如:如果團隊規模是 10 個人支撐業務,按照「三個火槍手」規則計算,大約需要劃分為 4 個服務,那麼「登錄、註冊、用戶資訊管理」都可以划到「用戶服務」職責範圍內;如果團隊規模是 100 人支撐業務,服務數量可以達到 40 個,那麼「用戶登錄「就是一個服務了;如果團隊規模達到 1000 人支撐業務,那「用戶連接管理」可能就是一個獨立的服務了。 2. 基於可擴展拆分 將系統中的業務模塊按照穩定性排序,將已經成熟和改動不大的服務拆分為穩定服務,將經常變化和迭代的服務拆分為變動服務。穩定的服務粒度可以粗一些,即使邏輯上沒有強關聯的服務,也可以放在同一個子系統中,例如將「日誌服務」和「升級服務」放在同一個子系統中;不穩定的服務粒度可以細一些,但也不要太細,始終記住要控制服務的總數量。 這樣拆分主要是為了提升項目快速迭代的效率,避免在開發的時候,不小心影響了已有的成熟功能導致線上問題。 3. 基於可靠性拆分 將系統中的業務模塊按照優先級排序,將可靠性要求高的核心服務和可靠性要求低的非核心服務拆分開來,然後重點保證核心服務的高可用。具體拆分的時候,核心服務可以是一個也可以是多個,只要最終的服務數量滿足「三個火槍手」的原則就可以。 這樣拆分帶來下面幾個好處: `避免非核心服務故障影響核心服務` >例如,日誌上傳一般都屬於非核心服務,但是在某些場景下可能有大量的日誌上報,如果系統沒有拆分,那麼日誌上報可能導致核心服務故障;拆分後即使日誌上報有問題,也不會影響核心服務。 >核心服務高可用方案可以更簡單 >核心服務的功能邏輯更加簡單,存儲的資料可能更少,用到的組件也會更少,設計高可用方案大部分情況下要比不拆分簡單很多。 `能夠降低高可用成本` >將核心服務拆分出來後,核心服務佔用的機器、帶寬等資源比不拆分要少很多。因此,只針對核心服務做高可用方案,機器、帶寬等成本比不拆分要節省較多。 > 4. 基於性能拆分 基於性能拆分和基於可靠性拆分類似,將性能要求高或者性能壓力大的模塊拆分出來,避免性能壓力大的服務影響其他服務。常見的拆分方式和具體的性能瓶頸有關,可以拆分 Web 服務、資料庫、緩存等。例如電商的搶購,性能壓力最大的是入口的排隊功能,可以將排隊功能獨立為一個服務。 以上幾種拆分方式不是多選一,而是可以根據實際情況自由排列組合,例如可以基於可靠性拆分出服務 A,基於性能拆分出服務 B,基於可擴展拆分出 C/D/F 三個服務,加上原有的服務 X,最後總共拆分出 6 個服務(A/B/C/D/F/X)。 ##### 基礎設施 大部分人主要關注的是微服務的「small」和「lightweight」特性,但實際上真正決定微服務成敗的,恰恰是那個被大部分人都忽略的「automated」。當服務粒度即使劃分不合理,實際落地後如果團隊遇到麻煩,自然會想到拆服務或者合服務;如果「automated」相關的基礎設施不健全,那微服務就是焦油坑,讓研發、測試、運維陷入各種微服務陷阱中(**`服務劃分過細,服務間關係複雜、服務數量太多,團隊效率急劇下降、調用鍊太長,問題定為困難、沒有自動化支撐,無法快速交付,沒有服務治理,微服務數量過多導致後台管理混亂`**)。 通常情況下,按照下面優先級來搭建基礎設施: 1. 服務發現、服務路由、服務容錯:這是最基本的微服務基礎設施。 2. 接口框架、API 網關:主要是為了提升開發效率,接口框架是提升內部服務的開發效率,API 網關是為了提升與外部服務對接的效率。 3. 自動化部署、自動化測試、配置中心:主要是為了提升測試和運維效率。 4. 服務監控、服務跟蹤、服務安全:主要是為了進一步提升運維效率。 5. 以上 3 和 4 兩類基礎設施,其重要性會隨著微服務節點數量增加而越來越重要,但在微服務節點數量較少的時候,可以通過人工的方式支撐,雖然效率不高,但也基本能夠維持一定運作。 * * * #### 基礎設施 ##### 自動化測試 微服務將原本大一統的系統拆分為多個獨立運行的「微」服務,微服務之間的接口數量大大增加,並且微服務提倡快速交付,版本週期短,版本更新頻繁。如果每次更新都靠人工回歸整個系統,則工作量大,效率低下,達不到「快速交付」的目的,因此必須通過自動化測試系統來完成絕大部分測試回歸的工作。 自動化測試涵蓋的範圍包括代碼級的單元測試、單個系統級的集成測試、系統間的接口測試,理想情況是每類測試都自動化。如果因為團隊規模和人力的原因無法全面覆蓋,至少要做到接口測試自動化。 ##### 自動化部署 相比大一統的系統,微服務需要部署的節點增加了幾倍甚至十幾倍,微服務部署的頻率也會大幅提升(一般公司業務系統約 50% 的工作日都有部署操作),綜合計算下來,微服務部署的次數是單體系統部署次數的幾十倍。這麼大量的部署操作,如果繼續採用人工手工處理,需要投入大量的人力,且容易出錯,因此需要自動化部署的系統來完成部署操作。 自動化部署系統包括版本管理、資源管理(例如,機器管理、虛擬機管理)、部署操作、回退操作等功能。 ##### 配置中心 微服務的節點數量非常多,通過人工登錄每台機器手工修改,效率低,容易出錯。特別是在部署或者排障時,需要快速增刪改查配置,人工操作的方式顯然是不行的。除此以外,有的運行期配置需要動態修改並且所有節點即時生效,人工操作是無法做到的。綜合上面的分析,微服務需要一個統一的配置中心來管理所有微服務節點的配置。 配置中心包括**配置版本管理**(例如,同樣的微服務,有 10 個節點是給A用戶群服務的,有 20 個節點給B用戶群服務的,配置項目都一樣,配置值不一樣)、**增刪改查配置、節點管理、配置同步、配置推送等功能**。 ##### 接口框架 微服務提倡輕量級的通信方式,一般採用 HTTP/REST 或者 RPC 方式統一接口協議。但在實踐過程中,光統一接口協議還不夠,還需要統一接口傳遞的資料格式。例如,我們需要指定接口協議為 HTTP/REST,但這還不夠,還需要指定 HTTP/REST 的資料格式採用 JSON,並且 JSON 的資料都遵循如下規範。 如果只是簡單指定了 HTTP/REST 協議,而不指定 JSON 和 JSON 的資料規範,那麼就會出現這樣混亂的情況:有的微服務採用 XML,有的採用 JSON,有的採用鍵值對;即使同樣都是 JSON,JSON 資料格式也不一樣。這樣每個微服務都要適配幾套甚至幾十套接口協議,相當於把曾經由 ESB 做的事情轉交給微服務自己做了,這樣做的效率顯然是無法接受的,因此需要統一接口框架。 ##### 服務發現 微服務種類和數量很多,如果這些資訊全部通過手工配置的方式寫入各個微服務節點,首先配置工作量很大,配置文件可能要配幾百上千行,幾十個節點加起來後配置項就是幾萬幾十萬行了,人工維護這麼大數量的配置項是一項災難;其次是微服務節點經常變化,可能是由於擴容導致節點增加,也可能是故障處理時隔離掉一部分節點,還可能是採用灰度升級,先將一部分節點升級到新版本,然後讓新老版本同時運行。不管哪種情況,我們都希望節點的變化能夠及時同步到所有其他依賴的微服務。如果採用手工配置,是不可能做到實時更改生效的。因此,需要一套服務發現的系統來支撐微服務的自動註冊和發現。 ##### 服務路由 有了服務發現後,微服務之間能夠方便地獲取相關配置資料,但具體進行某次調用請求時,我們還需要從所有符合條件的可用微服務節點中挑選出一個具體的節點發起請求,這就是服務路由需要完成的功能。 服務路由和服務發現緊密相關,服務路由一般不會設計成一個獨立運行的系統,通常情況下是和服務發現放在一起實現的。對於自理式服務發現,服務路由是微服務內部實現的;對於代理式服務發現,服務路由是由 LOAD BALANCER 系統實現的。無論放在哪裡實現,服務路由核心的功能就是路由算法。 >常見的路由算法有:隨機路由、輪詢路由、最小壓力路由、最小連接數路由等。 ##### 服務容錯 系統拆分為微服務後,單個微服務故障的概率變小,故障影響範圍也減少,但是微服務的節點數量大大增加。從整體上來看,系統中某個微服務出故障的概率會大大增加。如果不及時處理故障,故障擴散開來就會導致看起來系統中很多服務節點都故障了,因此需要微服務能夠自動應對這種出錯場景,及時進行處理。否則,如果節點一故障就需要人工處理,投入人力大,處理速度慢;而一旦處理速度慢,則故障就很快擴散,所以我們需要服務容錯的能力。 >常見的服務容錯包括請求重試、流控和服務隔離。通常情況下,服務容錯會集成在服務發現和服務路由系統中。 ##### 服務監控 系統拆分為微服務後,節點數量大大增加,導致需要監控的機器、網絡、進程、接口調用數等監控對象的數量大大增加;同時,一旦發生故障,我們需要快速根據各類資訊來定位故障。這兩個目標如果靠人力去完成是不現實的。舉個簡單例子:我們收到用戶投訴說業務有問題,如果此時採取人工的方式去蒐集、分析資訊,可能把幾十個節點的日誌打開一遍就需要數十幾分鐘了,因此需要服務監控系統來完成微服務節點的監控。 `服務監控的主要作用有:` >實時蒐集資訊並進行分析,避免故障後再來分析,減少了處理時間。 >服務監控可以在實時分析的基礎上進行預警,在問題萌芽的階段發覺並預警,降低了問題影響的範圍和時間。 >通常情況下,服務監控需要蒐集並分析大量的資料,因此建議做成獨立的系統,而不要集成到服務發現、API 網關等系統中。 ##### 服務跟蹤 服務監控可以做到微服務節點級的監控和資訊收集,但如果我們需要跟蹤某一個請求在微服務中的完整路徑,服務監控是難以實現的。因為如果每個服務的完整請求鏈資訊都實時發送給服務監控系統,資料量會大到無法處理。 服務監控和服務跟蹤的區別可以簡單概括為宏觀和微觀的區別。例如,A 服務通過 HTTP 協議請求 B 服務 10 次,B 通過 HTTP 返回 JSON 對象,服務監控會記錄請求次數、響應時間平均值、響應時間最高值、錯誤碼分布這些資訊;而服務跟蹤會記錄其中某次請求的發起時間、響應時間、響應錯誤碼、請求參數、返回的 JSON 對象等資訊。 >目前無論是分布式跟蹤還是微服務的服務跟蹤,絕大部分請求跟蹤的實現技術都基於 Google 的 Dapper 論文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》[3]。 ##### 服務安全 系統拆分為微服務後,資料分散在各個微服務節點上。從系統連接的角度來說,任意微服務都可以訪問所有其他微服務節點;但從業務的角度來說,部分敏感資料或者操作,只能部分微服務可以訪問,而不是所有的微服務都可以訪問,因此需要設計服務安全機制來保證業務和資料的安全性。 >服務安全主要分為三部分:接入安全、資料安全、傳輸安全。通常情況下,服務安全可以集成到配置中心系統中進行實現,即配置中心配置微服務的接入安全策略和資料安全策略,微服務節點從配置中心獲取這些配置資訊,然後在處理具體的微服務調用請求時根據安全策略進行處理。由於這些策略是通用的,一般會把策略封裝成通用的庫提供給各個微服務調用。 #### 參考資料 * * * 1.https://archerzdip.github.io/微服务架构系列-实战篇-服务拆分原则及系统架构设计/ 2.https://daimajiaoliu.com/daima/4de297e0c100400 3.https://research.google/pubs/pub36356/