微服務架構最佳實踐
- 避免非核心服務故障影響核心服務
例如,日誌上報一般都屬於非核心服務,但是在某些場景下可能有大量的日誌上報,如果系統沒有拆分,那麼日誌上報可能導致核心服務故障;拆分後即使日誌上報有問題,也不會影響核心服務
- 核心服務高可用方案可以更加單
核心服務的功能邏輯更加簡單,儲存的資料可能更少,用到的元件也會更少,設計高可用方案部分情況下要比不拆分簡單很多
- 能夠降低高可用成本
將核心服務拆分出來後,核心服務佔用的機器、頻寬等資源比不拆分要少很多。因此,只針對核心服務做高可用方案,機器、頻寬等成本比不拆分要節省較多
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系統實現的
- 無論放在哪裡實現,服務路由核心的功能就是路由演算法。常見的路由演算法有:隨機路由、輪詢路由、最小壓力路由、最小連線數路由等