微服務架構最佳實踐

  • 注:文章來源:極客時間的專欄《從0開始學架構》

方法篇

服務粒度

  • 三個火槍手原則,即一個微服務三我的負責開發
  • 從系統規模來說,3我的負責開發一個系統,系統的複雜度恰好達到每一個人都能全面理解整個系統,又可以進行分工的粒度;2我的,系統的複雜度不夠,開發人員可能以爲沒法體現本身的技術實力;4個及以上,系統複雜度又沒法讓開發人員對系統的細節都瞭解很深
  • 從團隊管理來講,3我的能夠造成一個穩定的備份,即便一我的休假或者調配到其餘系統,剩餘2我的還能夠支撐;2我的壓力太大;一我的就是單點啦

拆分方法

  • 基於「三個火槍手」的理論,能夠計算出拆分後合適的服務數量
1. 基於業務邏輯拆分
  • 將系統中的業務模塊按照職責範圍識別出來,每一個單獨的業務模塊拆分爲一個獨立的服務
  • 難點問題在於,對「職責範圍」的理解差別很大。例如,一個電商系統,第一種方式是將服務劃分爲「商品」「交易」「用戶」3個服務,第二種方式是劃分爲「商品」「訂單」「支付」「發貨」「賣家」「買家」6個服務,哪一種方式更合理?
  • 困惑在於從業務的角度來拆分,規模粗和細都沒有問題,由於拆分基礎都是業務邏輯,要判斷拆分粒度,不能從業務邏輯角度,根據「三個火槍手」原則,計算一下大概的服務範圍
  • 例如,有10我的,按以上原則,大約須要劃分4個服務,那麼「登陸、註冊、用戶信息管理」均可以劃到「用戶服務」職責範圍內;若是團隊規模是100人支撐服務,服務數量能夠達到40個,那麼「用戶登陸」就是一個服務了;若是團隊規模達到1000人支撐業務,那「用戶鏈接管理」可能就是一個獨立的服務了
2. 基於可擴展拆分
  • 將系統中的業務模塊按照穩定性排序,將已經成熟和改動不大的服務拆分爲穩定服務,將常常變化和迭代的服務拆分爲變更服務
  • 穩定的服務粒度能夠粗一些,即便邏輯上沒有強關聯的服務,也能夠放在同一個子系統中,例如將「日誌服務」和「升級服務」放在同一個子系統中;不穩定的服務粒度能夠細一些,但不要太細,始終記住要控制服務的總數量
  • 這樣的拆分主要是爲了提高項目快速迭代的效率,避免在開發的時候,不當心影響了已有的成熟功能致使線上問題
3. 基於可靠性拆分
  • 將系統中的業務模塊按照優先級排序,將可靠性要求高的核心服務和要求低的非核心服務拆分開來,而後重點保證核心服務的高可用。
好處
  • 避免非核心服務故障影響核心服務

  例如,日誌上報通常都屬於非核心服務,可是在某些場景下可能有大量的日誌上報,若是系統沒有拆分,那麼日誌上報可能致使核心服務故障;拆分後即便日誌上報有問題,也不會影響核心服務算法

  • 核心服務高可用方案能夠更加單

  核心服務的功能邏輯更加簡單,存儲的數據可能更少,用到的組件也會更少,設計高可用方案部分狀況下要比不拆分簡單不少數據庫

  • 可以下降高可用成本

  將核心服務拆分出來後,核心服務佔用的機器、帶寬等資源比不拆分要少不少。所以,只針對核心服務作高可用方案,機器、帶寬等成本比不拆分要節省較多緩存

4. 基於性能拆分
  • 將性能要求高或者性能壓力大的模塊拆分出來,避免性能壓力大的服務影響其餘服務
  • 常見的拆分方式和具體的性能瓶頸有關,能夠拆分Web服務、數據庫、緩存等
  • 例如,電商的搶購,性能壓力最大的是入口的排隊功能,能夠將排隊功能獨立爲一個服務
以上拆分,能夠根據實際狀況自由排列組合

基礎設施

  • 「automated」是重要一環,若是其相關的基礎設施不健全,那微服務就是焦油坑,讓研發,測試,運維陷入各類陷阱中
  • 微服務基礎設施以下圖所示

實施微服務
  • 有開源的微服務基礎設施全家桶,例如,Spring Cloud項目,涵蓋了服務發現、服務路由、網關、配置中心等功能
  • 若是微服務的數量並非不少的話,並非每一個基礎設施都是必須的
按優先級來搭建基礎設施
    1. 服務發現、服務路由、服務容錯:這是最基本的微服務基礎設施
    1. 接口框架、API網關:主要是爲了提高開發效率,接口框架是提高內部服務的開發效率,API網關是爲了提高與外部服務對接的效率
    1. 自動化部署、自動化測試、配置中心:主要是爲了提高測試和運維效率
    1. 服務監控、服務跟蹤、服務安全:主要是爲了進一步提高運維效率
  • 以上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》

服務安全

  • 系統拆分爲微服務後,數據分散在各個微服務節點上
  • 從系統鏈接的角度來講,任意微服務均可以訪問全部其餘微服務節點
  • 從業務的角度來講,部分敏感數據或者操做,只能部分微服務能夠訪問,而不是全部的微服務均可以訪問,所以須要設計服務安全機制來保證業務和數據的安全性
  • 服務安全主要分爲三部分:接入安全、數據安全、傳輸安全
  • 一般狀況下,服務安全能夠集成到配置中心繫統中進行實現,即配置中心配置微服務的接入安全策略和數據安全策略,微服務節點從配置中心獲取這些配置信息,而後在處理具體的微服務調用請求時根據安全策略進行處理。
  • 因爲這些策略是通用的,通常會把策略封裝通用的庫提供給各個微服務調用。
  • 基本架構以下:

注:有興趣瞭解極客時間專欄的同窗,能夠查看極客時間專欄—可提供返現服務網絡