- 注:文章來源:極客時間的專欄《從0開始學架構》
方法篇
服務粒度
- 三個火槍手原則,即一個微服務三個人負責開發
- 從系統規模來講,3個人負責開發一個系統,系統的複雜度剛好達到每個人都能全面理解整個系統,又能夠進行分工的粒度;2個人,系統的複雜度不夠,開發人員可能覺得無法體現自己的技術實力;4個及以上,系統複雜度又無法讓開發人員對系統的細節都瞭解很深
- 從團隊管理來說,3個人可以形成一個穩定的備份,即使一個人休假或者調配到其他系統,剩餘2個人還可以支撐;2個人壓力太大;一個人就是單點啦
拆分方法
- 基於“三個火槍手”的理論,可以計算出拆分後合適的服務數量
1. 基於業務邏輯拆分
- 將系統中的業務模組按照職責範圍識別出來,每個單獨的業務模組拆分為一個獨立的服務
- 難點問題在於,對“職責範圍”的理解差異很大。例如,一個電商系統,第一種方式是將服務劃分為“商品”“交易”“使用者”3個服務,第二種方式是劃分為“商品”“訂單”“支付”“發貨”“賣家”“買家”6個服務,哪種方式更合理?
- 困惑在於從業務的角度來拆分,規模粗和細都沒有問題,因為拆分基礎都是業務邏輯,要判斷拆分粒度,不能從業務邏輯角度,根據“三個火槍手”原則,計算一下大概的服務範圍
- 例如,有10個人,按以上原則,大約需要劃分4個服務,那麼“登入、註冊、使用者資訊管理”都可以劃到“使用者服務”職責範圍內;如果團隊規模是100人支撐服務,服務數量可以達到40個,那麼“使用者登入”就是一個服務了;如果團隊規模達到1000人支撐業務,那“使用者連線管理”可能就是一個獨立的服務了
2. 基於可擴充套件拆分
- 將系統中的業務模組按照穩定性排序,將已經成熟和改動不大的服務拆分為穩定服務,將經常變化和迭代的服務拆分為變動服務
- 穩定的服務粒度可以粗一些,即使邏輯上沒有強關聯的服務,也可以放在同一個子系統中,例如將“日誌服務”和“升級服務”放在同一個子系統中;不穩定的服務粒度可以細一些,但不要太細,始終記住要控制服務的總數量
- 這樣的拆分主要是為了提升專案快速迭代的效率,避免在開發的時候,不小心影響了已有的成熟功能導致線上問題
3. 基於可靠性拆分
- 將系統中的業務模組按照優先順序排序,將可靠性要求高的核心服務和要求低的非核心服務拆分開來,然後重點保證核心服務的高可用。
好處
- 避免非核心服務故障影響核心服務
例如,日誌上報一般都屬於非核心服務,但是在某些場景下可能有大量的日誌上報,如果系統沒有拆分,那麼日誌上報可能導致核心服務故障;拆分後即使日誌上報有問題,也不會影響核心服務
- 核心服務高可用方案可以更加單
核心服務的功能邏輯更加簡單,儲存的資料可能更少,用到的元件也會更少,設計高可用方案部分情況下要比不拆分簡單很多
- 能夠降低高可用成本
將核心服務拆分出來後,核心服務佔用的機器、頻寬等資源比不拆分要少很多。因此,只針對核心服務做高可用方案,機器、頻寬等成本比不拆分要節省較多
4. 基於效能拆分
- 將效能要求高或者效能壓力大的模組拆分出來,避免效能壓力大的服務影響其他服務
- 常見的拆分方式和具體的效能瓶頸有關,可以拆分Web服務、資料庫、快取等
- 例如,電商的搶購,效能壓力最大的是入口的排隊功能,可以將排隊功能獨立為一個服務
以上拆分,可以根據實際情況自由排列組合
基礎設施
- “automated”是重要一環,如果其相關的基礎設施不健全,那微服務就是焦油坑,讓研發,測試,運維陷入各種陷阱中
- 微服務基礎設施如下圖所示
實施微服務
- 有開源的微服務基礎設施全家桶,例如,Spring Cloud專案,涵蓋了服務發現、服務路由、閘道器、配置中心等功能
- 如果微服務的數量並不是很多的話,並不是每個基礎設施都是必須的
按優先順序來搭建基礎設施
-
- 服務發現、服務路由、服務容錯:這是最基本的微服務基礎設施
-
- 介面框架、API閘道器:主要是為了提升開發效率,介面框架是提升內部服務的開發效率,API閘道器是為了提升與外部服務對接的效率
-
- 自動化部署、自動化測試、配置中心:主要是為了提升測試和運維效率
-
- 服務監控、服務跟蹤、服務安全:主要是為了進一步提升運維效率
- 以上3和4兩類基礎設施,其重要性會隨著微服務節點數量增加而越來越重要,但在微服務節點數量較少的時候,可以通過人工的方式支撐,雖然效率不高,但也基本能夠頂住
基礎設施
自動化測試
- 微服務將原本大一統的系統拆分為多個獨立執行的“微”服務,微服務之間的介面數量大大增加,並且微服務提倡快速交付,版本週期短,版本更新頻繁
- 如果每次更新都靠人工迴歸整個系統,則工作量大,效率低下,達不到“快速交付”的目的,因此必須通過自動化測試系統來完成絕大部分測試迴歸的工作中
- 自動化測試涵蓋的範圍包括程式碼級的單元測試、單個系統級的整合測試、系統間的介面測試,理想情況是每類測試都是自動化
- 因為團隊規模和人力的原因無法全面覆蓋,至少要做到介面測試自動化
自動化部署
- 相比大一統的系統,微服務需要部署的節點增加了幾倍甚至十幾倍,微服務部署的頻率也會大幅提升(例如,我們的業務系統70%的工作日都部署操作),綜合計算下來,微服務部署的次數是大一統系統部署次數的幾十倍
- 這麼大的部署操作,如果繼續採用人工手工處理,需要投入大量的人力,且容易出錯,因此需要自動化部署的系統來完成部署操作
- 自動化部署系統包括版本管理、資源管理(例如,機器管理、虛擬機器管理)、部署操作、回退操作等功能
配置中心
- 微服務的節點數量非常多,通過人工登入每臺機器手工修改,效率低,容易出錯
- 特別是部署或者排障時,需要快速增刪改查配置,人工操作的方式顯然是不行的
- 有的執行期配置需要動態修改並且所有節點即時生效,人工操作是無法做到的
- 綜上,微服務需要一個統一的配置中心來管理所有微服務節點的配置
- 配置中心包括配置版本管理(例如,同樣的微服務,有10個節點是給移動使用者服務的,有20個節點給聯通使用者服務的,配置項都一樣,配置值不一樣)、增刪改查配置、節點配置、配置同步、配置推送等功能
介面框架
- 微服務提倡輕量級的通訊方式,一般採用HTTP/REST或者RPC方式統一介面協議
- 但在實踐過程中,光統一介面協議還不夠,還需要統一介面傳遞的資料格式
- 例如,我們需要指定介面協議為HTTP/REST,但這還不夠,還需要指定HTTP/REST的資料格式採用JSON,並且JSON的資料都遵循如下規範。
- 如果我們只是簡單指定了HTTP/REST協議,而不指定JSON和JSON的資料規範,那麼就會出現這樣混亂的情況:有的微服務採用XML,有的採用JSON,有的採用鍵值對;即使同樣都是JSON,JSON資料格式也不一樣。這樣每個微服務都要適配幾套甚至幾十套介面協議,相當於把曾經由ESB做的事情轉交給微服務自己做了,這樣做的效率顯然是無法接受的,因此需要統一介面框架
- 介面框架不是一個可執行的系統,一般以庫或者包的形式提供給所有微服務呼叫。例如,針對上面的JSON樣例,可以由某個基礎技術團隊提供多種不同語言的解析包(Java包、Python包 、C庫等)
API閘道器
- 系統拆分為微服務後,內部的微服務之間是互聯互通的,相互之間的訪問都是點對點的
- 如果外部系統想呼叫系統的某個功能,也採取點對點的方式,則外部系統會非常“頭大”
- 因為在外部系統看來,它不需要也沒辦法理解這麼多微服務的職責分工和邊界,它只會關注它需要的能力,而不會關注這個能力應該由哪個微服務提供
- 外部系統訪問系統還涉及安全和許可權相關的限制,如果外部系統直接訪問某個微服務,則意味著每個微服務都要自己實現安全和許可權的功能,這樣做不但工作量大,而且都是重複工作
- 綜合上面的分析,微服務需要一個統一的API閘道器,負責外部系統的訪問操作
- API閘道器是外部系統訪問的介面,所有的外部系統接入系統都需要通過API閘道器,主要包括接入鑑權(是否允許接入)、許可權控制(可以訪問哪些功能)、傳輸加密、請求路由、流量控制等功能
服務發現
- 微服務種類和數量很多,如果這些資訊全部通過手工配置的方式寫入各個微服務節點,首先配置工作量大,配置檔案可能要配幾百上千行,幾十個節點加起來後配置項就是幾萬幾十萬行了,人工維護這麼大數量的配置項是一項災難
- 其次是微服務節點經常變化,可能是由於擴容導致節點增加,也可能是故障處理時隔離掉一部分節點,還可能是採用灰度升級,先將一部分節點升級到新版本,然後讓新老版本同時執行
- 不管哪種情況,我們都希望節點的變化能夠及時同步到所有其他依賴的微服務。如果採用手工配置,是不可能做到實時更改生效的
- 因此,需要一套服務發現的系統來支撐微服務的自動註冊和發現
服務發現主要有兩種實現方式:自理式和代理式
1. 自理式
- 自理式結構如下:
- 自理式結構就是指每個微服務自己完成服務發現。例如,圖中SERVICE INSTANCE A訪問SERVICE REGISTRY獲取服務註冊資訊,然後直接訪問SERVICE INSTANCE B
- 自理式服務發現實現比較簡單,因為這部分的功能一般通過統一的程式庫或者程式包提供給各個微服務呼叫,而不會每個微服務都自己來重複實現一遍;並且由於每個微服務都承擔了服務發現的功能,訪問壓力分散到了各個微服務節點,效能和可用性上不存在明顯的壓力和風險
2. 代理式
- 代理式結構如下:
- 代理式結構就是指微服務之間有一個負載均衡系統,由負載均衡系統來完成微服務之間的服務發現
- 代理式的方式看起來更加清晰,微服務本身的實現也簡單了很多,但實際上這個方案風險較大
- 第一個風險是可用性風險,一旦LOAD BALANCER系統故障,就會影響所有微服務之間的呼叫
- 第二個風險是效能風險,所有的微服務之間的呼叫流量都要經過LOAD BALANCER系統,效能壓力會隨著微服務數量和流量增加而不斷增加,最後成為效能瓶頸
- 因此LOAD BALANCER系統需要設計成叢集的模式,但LOAD BALANCER叢集的實現本身又增加了複雜性
- 不管是自理式還是代理式,服務發現的核心功能就是服務登錄檔,登錄檔記錄了所有的服務節點的配置和狀態,每個微服務啟動後都需要將自己的資訊註冊到服務登錄檔,然後由微服務或者LOAD BALANCER系統到服務登錄檔查詢可用服務
服務路由
- 有了服務發現之後,微服務之間能夠方便地獲取相關配置資訊,但具體進行某次呼叫請求時,我們還需要從所有符合條件的可用微服務節點中挑選出一個具體的節點發起請求,這就是服務路由需要完成的功能
- 服務路由和服務發現緊密相關,服務路由一般不會設計成一個獨立執行的系統,通常情況下是和服務發現放在一起實現的
- 對於自理式服務發現,服務路由是微服務內部實現的;對於代理式服務發現,服務路由是由LOAD BALANCER系統實現的
- 無論放在哪裡實現,服務路由核心的功能就是路由演算法。常見的路由演算法有:隨機路由、輪詢路由、最小壓力路由、最小連線數路由等
服務容錯
- 系統拆分為微服務後,單個微服務故障的概率變小,故障影響的範圍也減少,但是微服務的節點數量大大增加
- 從整體上來看,系統中某個微服務出故障的概率會大大增加
- 微服務具有故障擴散的特點,如果不及時處理故障,故障擴散開來就會導致看起來系統中很多服務節點都故障了, 因此需要微服務能夠自動應對這種出錯場景,及時進行處理。否則,如果節點一故障就需要人工處理,投入人力大,處理速度慢;而一旦處理速度慢,則故障就很快擴散,所以我們需要服務容錯的能力
- 常見的服務容錯包括請求重試、流控和服務隔離
- 通常情況下,服務容錯會整合在服務發現和服務路由系統中
服務監控
- 系統拆分微服務後,節點數量大大增加,導致需要監控的機器、網路、程式、介面呼叫數等控制物件的數量大大增加;同時,一旦發生故障,我們需要快速根據各類資訊來定位故障。靠人力去完成是不現實的
- 例如,我們收到使用者投訴說業務有問題,如果此時採用人工的方式去搜集、分析資訊,可能把幾十個節點的日誌開啟一遍就需要十幾分鍾了,因此需要服務監控系統來完成微服務節點的監控
作用
- 實施蒐集資訊並進行分析,避免故障後再來分析,減少了處理時間
- 服務監控可以在實時分析的基礎上進行預警,在問題萌芽的階段發覺並預警,降低了問題影響的範圍和時間
通常情況下,服務監控需要蒐集分析大量的資料,因此建議做成獨立的系統,而不要整合到服務發現、API閘道器等系統中
服務跟蹤
- 服務監控可以做到微服務節點級的監控和資訊收集,但如果我們需要跟蹤某一個請求在微服務中的完整路徑,服務監控是難以實現的。如果每個服務的完整請求鏈資訊都實時傳送給服務監控系統中,資料量會大到無法處理
- 服務監控和服務跟蹤的區別可以簡單概括為巨集觀和微觀的區別。例如,A服務通過HTTP協議請求B服務10次,B通過HTTP返回JSON物件,服務監控會記錄請求次數、響應時間、響應錯誤碼、請求引數、返回的JOSN物件等資訊
- 目前無論是分散式跟蹤還是微服務的服務跟蹤,絕大部分請求跟蹤的實現技術都基於Google的Dapper論文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》
服務安全
- 系統拆分為微服務後,資料分散在各個微服務節點上
- 從系統連線的角度來說,任意微服務都可以訪問所有其他微服務節點
- 從業務的角度來說,部分敏感資料或者操作,只能部分微服務可以訪問,而不是所有的微服務都可以訪問,因此需要設計服務安全機制來保證業務和資料的安全性
- 服務安全主要分為三部分:接入安全、資料安全、傳輸安全
- 通常情況下,服務安全可以整合到配置中心繫統中進行實現,即配置中心配置微服務的接入安全策略和資料安全策略,微服務節點從配置中心獲取這些配置資訊,然後在處理具體的微服務呼叫請求時根據安全策略進行處理。
- 由於這些策略是通用的,一般會把策略封裝通用的庫提供給各個微服務呼叫。
- 基本架構如下:
注:有興趣瞭解極客時間專欄的同學,可以檢視極客時間專欄—可提供返現服務