從 0 開始學微服務
說起微服務,大家應該並不陌生,不只是一線大廠,很多中小規模團隊也已經將這項技術引入並在實際業務中落地。
那作爲一名開發人員,應該如何學習微服務呢?
雖然現在開源的微服務框架有很多,各種編程語言的都有,花上幾個小時搭建一套可運行的開發環境也並不是一件難事。但畢竟微服務涉及的組件還是挺多的,相比於單體架構來說,複雜度提升了不少。
不知道你有沒有和我一樣的困擾,有時候想要深入學習一下,但卻不知道從什麼地方入手,結果就是對其瞭解只是浮於表面。
所以我最近看了極客時間的專欄《從 0 開始學微服務》,覺得作爲入門還是不錯的。
同時,我總結了專欄中的部分內容,並加入一些自己的思考,形成了這篇文章。算是學習微服務的一個大綱,然後再按照這個大綱去深入學習,不斷充實這套技術體系。
好了,下面正文開始。
什麼是微服務
微服務是指開發應用所用的一種架構形式。通過微服務,可將大型應用分解成多個獨立的組件,其中每個組件都有各自的責任領域。在處理一個用戶請求時,基於微服務的應用可能會調用許多內部微服務來共同生成其響應。
微服務的特點:
-
服務拆分粒度: 小到一個子模塊,只要該模塊依賴的資源與其他模塊都沒有關係,那麼就可以拆分爲一個微服務。
-
服務獨立部署: 每個微服務都嚴格遵循獨立打包部署的準則,互不影響。比如一臺物理機上可以部署多個 Docker 實例,每個 Docker 實例可以部署一個微服務的代碼。
-
服務獨立維護: 每個微服務都可以交由一個小團隊甚至個人來開發、測試、發佈和運維,並對整個生命週期負責。
-
服務治理能力要求高: 因爲拆分爲微服務之後,服務的數量變多,因此需要有統一的服務治理平臺,來對各個服務進行管理。
服務發佈和引用
想要構建微服務,首先要解決的問題是,服務提供者如何發佈一個服務,服務消費者如何引用這個服務。具體來說,就是這個服務的接口名是什麼?調用這個服務需要傳遞哪些參數?接口的返回值是什麼類型?以及一些其他接口描述信息。
最常見的服務發佈和引用的方式有三種:
-
RESTful API
-
XML 配置
-
IDL 文件
RESTful API
這種方式就比較常見了,主要被用作 HTTP 或者 HTTPS 協議的接口定義,即使在非微服務架構體系下,也被廣泛採用。而且學習成本低,比較適合用作跨業務平臺之間的服務協議。
XML 配置
這種方式下需要在服務提供者和服務消費者之間維持一份對等的 XML 配置文件,來保證服務調用。比如提供者維護 server.xml,消費者維護 client.xml。
在接口變更時,需要同時修改這兩個接口描述文件。
IDL 文件
IDL 就是接口描述語言(interface description language)的縮寫,通過一種中立的方式來描述接口,使得在不同的平臺上運行的對象和不同語言編寫的程序可以相互通信交流。
也就是說 IDL 主要是用作跨語言平臺的服務之間的調用,有兩種最常用的 IDL:一個是 Facebook 開源的 Thrift 協議,另一個是 Google 開源的 gRPC 協議。
小結
每種方式都有各自的優缺點,具體應該如何選擇,需要根據自身的業務特點。
註冊中心
服務拆分之後,服務的提供者和消費者分別運行在不同的機器上,而且服務衆多,有幾十個,甚至上百個。這就涉及到一個問題,消費者如何才能找到服務提供者,這也是註冊中心需要解決的問題。
註冊中心需要實現哪些 API?
-
服務註冊接口: 服務提供者通過調用服務註冊接口來完成服務註冊。
-
服務反註冊接口: 服務提供者通過調用服務反註冊接口來完成服務註銷。
-
心跳彙報接口: 服務提供者通過調用心跳彙報接口完成節點存活狀態上報。
-
服務訂閱接口: 服務消費者通過調用服務訂閱接口完成服務訂閱,獲取可用的服務提供者節點列表。
-
服務變更查詢接口: 服務消費者通過調用服務變更查詢接口,獲取最新的可用服務節點列表。
除此之外,爲了便於管理,註冊中心還必須提供一些後臺管理的 API,例如:
-
服務查詢接口: 查詢註冊中心當前註冊了哪些服務信息。
-
服務修改接口: 修改註冊中心中某一服務的信息。
配置中心
在拆分爲微服務架構前,單體應用只需要管理一套配置;而拆分爲微服務後,每一個系統都有自己的配置,並且都各不相同,而且因爲服務治理的需要,有些配置還需要能夠動態改變,以達到動態降級、切流量、擴縮容等目的,所以如何管理配置十分重要。
配置中心一般包含下面幾個功能:
-
配置註冊
-
配置反註冊
-
配置查看
-
配置變更訂閱
API 網關
API 網關可以被視爲一種充當應用程序服務和不同客戶端之間的中間件,可以管理許多事情,例如:
-
路由: 網關接收所有 API 請求並將它們轉發到目標服務
-
記錄日誌: 能夠在一處記錄所有請求
-
權限認證: 檢查用戶是否有資格訪問該服務,如果沒有,可以拒絕該請求
-
性能分析: 估計每個請求的執行時間並檢查性能瓶頸
-
緩存: 通過在網關級別處理緩存,可以降低服務的流量壓力
事實上,它是作爲一個反向代理工作的,客戶端只需要知道系統的網關,應用服務就可以隱藏起來,不直接向其他系統暴露。
如果沒有 API 網關,可能需要在每個服務中做一些橫切關注點,比如想記錄服務的請求和響應。此外,如果應用程序由多個服務組成,客戶端需要知道每個服務地址,並且在更改服務地址的情況下,需要更新多個地方。
負載均衡
微服務架構擁有很好的可擴展性,我們能夠通過運行更多服務實例來處理更多請求,但問題是,哪個實例應該接收請求,或客戶端如何知道哪個服務實例應該處理請求?
這些問題的答案是負載均衡。負載均衡是高可用網絡基礎架構的關鍵組件,通常用於將工作負載分佈到多個服務器來提高網站、應用、數據庫或其他服務的性能和可靠性。
常用的負載均衡算法有一下五種:
隨機算法
顧名思義就是從可用的服務節點中,隨機挑選一個節點來訪問。
實現比較簡單,在請求量遠超可用服務節點數量的情況下,各個服務節點被訪問的概率基本相同,主要應用在各個服務節點的性能差異不大的情況下。
輪詢算法
跟隨機算法類似,各個服務節點被訪問的概率也基本相同,也主要應用在各個服務節點性能差異不大的情況下。
加權輪詢算法
在輪詢算法基礎上的改進,可以通過給每個節點設置不同的權重來控制訪問的概率,因此主要被用在服務節點性能差異比較大的情況。
比如經常會出現一種情況,因爲採購時間的不同,新的服務節點的性能往往要高於舊的節點,這個時候可以給新的節點設置更高的權重,讓它承擔更多的請求,充分發揮新節點的性能優勢。
最少活躍連接算法
與加權輪詢算法預先定義好每個節點的訪問權重不同,採用最少活躍連接算法,客戶端同服務端節點的連接數是在時刻變化的,理論上連接數越少代表此時服務端節點越空閒,選擇最空閒的節點發起請求,能獲取更快的響應速度。
尤其在服務端節點性能差異較大,而又不好做到預先定義權重時,採用最少活躍連接算法是比較好的選擇。
一致性 hash 算法
因爲它能夠保證同一個客戶端的請求始終訪問同一個服務節點,所以適合服務端節點處理不同客戶端請求差異較大的場景。
比如服務端緩存裏保存着客戶端的請求結果,如果同一客戶端一直訪問一個服務節點,那麼就可以一直從緩存中獲取數據。
服務監控
不管是單體架構,還是微服務架構,監控都是必不可少的。只不過微服務架構更加複雜,監控起來也就更加不容易。
對於一個微服務來說,必須明確要監控哪些對象、哪些指標,並且還要從不同的維度進行監控,才能掌握微服務的調用情況。
監控對象
監控對象可以分爲四個層次,由上到下可歸納爲:
-
用戶端監控: 通常是指業務直接對用戶提供的功能的監控。
-
接口監控: 通常是指業務提供的功能所依賴的具體 RPC 接口的監控。
-
資源監控: 通常是指某個接口依賴的資源的監控。
-
基礎監控: 通常是指對服務器本身的健康狀況的監控。主要包括 CPU 利用率、內存使用量、I/O 讀寫量、網卡帶寬等。
監控指標
搞清楚要監控的對象之後,需要監控具體哪些指標呢?
-
請求量: 請求量監控分爲兩個維度,一個是實時請求量,一個是統計請求量。實時請求量用 QPS(Queries Per Second)即每秒查詢次數來衡量,它反映了服務調用的實時變化情況。統計請求量一般用 PV(Page View)即一段時間內用戶的訪問量來衡量,比如一天的 PV 代表了服務一天的請求量,通常用來統計報表。
-
響應時間: 大多數情況下,可以用一段時間內所有調用的平均耗時來反映請求的響應時間。但它只代表了請求的平均快慢情況,有時候我們更關心慢請求的數量。爲此需要把響應時間劃分爲多個區間,比如 0~10ms、10ms~50ms、50ms~100ms、100ms~500ms、500ms 以上這五個區間,其中 500ms 以上這個區間內的請求數就代表了慢請求量,正常情況下,這個區間內的請求數應該接近於 0;在出現問題時,這個區間內的請求數會大幅增加,可能平均耗時並不能反映出這一變化。
-
錯誤率: 錯誤率的監控通常用一段時間內調用失敗的次數佔調用總次數的比率來衡量,比如對於接口的錯誤率一般用接口返回錯誤碼爲 503 的比率來表示。
監控維度
一般來說,要從多個維度來對業務進行監控,包括下面幾個維度:
-
全局維度: 從整體角度監控對象的的請求量、平均耗時以及錯誤率,全局維度的監控一般是爲了讓你對監控對象的調用情況有個整體瞭解。
-
分機房維度: 一般爲了業務的高可用性,服務通常部署在不止一個機房,因爲不同機房地域的不同,同一個監控對象的各種指標可能會相差很大,所以需要深入到機房內部去了解。
-
單機維度: 即便是在同一個機房內部,可能由於採購年份和批次的不同,位於不同機器上的同一個監控對象的各種指標也會有很大差異。一般來說,新採購的機器通常由於成本更低,配置也更高,在同等請求量的情況下,可能表現出較大的性能差異,因此也需要從單機維度去監控同一個對象。
-
時間維度: 同一個監控對象,在每天的同一時刻各種指標通常也不會一樣,這種差異要麼是由業務變更導致,要麼是運營活動導致。爲了瞭解監控對象各種指標的變化,通常需要與一天前、一週前、一個月前,甚至三個月前做比較。
-
核心維度: 業務上一般會依據重要性程度對監控對象進行分級,最簡單的是分成核心業務和非核心業務。核心業務和非核心業務在部署上必須隔離,分開監控,這樣才能對核心業務做重點保障。
服務追蹤
在調試單體應用時,非常直觀容易。但是在微服務架構上,因爲一個請求可能會通過不同的服務,而不同的服務又不在一個地方,這使得調試和跟蹤變得困難。
所以服務追蹤是分佈式系統中必不可少的功能,它能夠幫助我們查詢一次用戶請求在系統中的具體執行路徑,以及每一條路徑的上下游的詳細情況,對於追查問題十分有用。
它的核心理念就是調用鏈:通過一個全局唯一的 ID 將分佈在各個服務節點上的同一次請求串聯起來,從而還原原有的調用關係,可以追蹤系統問題、分析調用數據並統計各種系統指標。
-
traceId: 用於標識某一次具體的請求 ID。當用戶的請求進入系統後,會在 RPC 調用網絡的第一層生成一個全局唯一的 traceId,並且會隨着每一層的 RPC 調用,不斷往後傳遞,這樣的話通過 traceId 就可以把一次用戶請求在系統中調用的路徑串聯起來。
-
spanId: 用於標識一次 RPC 調用在分佈式請求中的位置。當用戶的請求進入系統後,處在 RPC 調用網絡的第一層 A 時 spanId 初始值是 0,進入下一層 RPC 調用 B 的時候 spanId 是 0.1,繼續進入下一層 RPC 調用 C 時 spanId 是 0.1.1,而與 B 處在同一層的 RPC 調用 E 的 spanId 是 0.2,這樣的話通過 spanId 就可以定位某一次 RPC 請求在系統調用中所處的位置,以及它的上下游依賴分別是誰。
-
annotation: 用於業務自定義埋點數據,可以是業務感興趣的想上傳到後端的數據,比如一次請求的用戶 UID。
小結一下,traceId 是用於串聯某一次請求在系統中經過的所有路徑,spanId 是用於區分系統不同服務之間調用的先後關係,而 annotation 是用於業務自定義一些自己感興趣的數據,在上傳 traceId 和 spanId 這些基本信息之外,添加一些自己感興趣的信息。
故障處理
系統故障是避免不了的,雖然微服務架構做了服務拆分,不至於像單體架構那樣整體崩潰,但由於其整體複雜度也大大提升,故障處理也更加困難。
限流
顧名思義,限流就是限制流量。通常情況下,系統能夠承載的流量根據集羣規模的大小是固定的,可以稱之爲系統的最大容量。
當真實流量超過了系統的最大容量後,就會導致系統響應變慢,服務調用出現大量超時,反映給用戶的感覺就是卡頓、無響應。
所以,應該根據系統的最大容量,給系統設置一個閾值,超過這個閾值的請求會被自動拋棄,這樣的話可以最大限度地保證系統提供的服務正常。
熔斷
熔斷和限流還不太一樣,上面我們可以看到限流是控制請求速率,只要還能承受,那麼都會處理,但熔斷不是。
在一條調用鏈上,如果發現某個服務異常,比如響應超時。那麼調用者爲了避免過多請求導致資源消耗過大,最終引發系統雪崩,會直接返回錯誤,而不是瘋狂調用這個服務。
降級
什麼是降級呢?降級就是通過停止系統中的某些功能,來保證系統整體的可用性。
降級可以說是一種被動防禦的措施,爲什麼這麼說呢?因爲它一般是系統已經出現故障後所採取的一種止損措施。
容器化
單體應用拆分成多個微服務後,能夠實現快速開發迭代,但隨之帶來的問題是測試和運維部署成本的提升。
而容器技術正好可以很好的解決這些問題,目前最流行的當屬 Docker 莫屬。
微服務容器化運維主要涉及到以下幾點:
-
鏡像倉庫
-
容器調度
-
服務編排
鏡像倉庫
鏡像倉庫的概念其實跟 Git 代碼倉庫類似,就是有一個集中存儲的地方,把鏡像存儲在這裏,在服務發佈的時候,各個服務器都訪問這個集中存儲來拉取鏡像,然後啓動容器。
容器調度
這個階段主要是解決在哪些機器上啓動容器的問題,特別是規模比較大的公司,一般有物理機集羣,虛擬機集羣,私有云和公有云。對接這麼多不同的平臺,難度還是不小的。
需要統一管理來自不同集羣的機器權限管理、成本覈算以及環境初始化等操作,這個時候就需要有一個統一的層來完成這個操作。
很顯然,靠人工是肯定不行的,需要搭建統一的部署運維平臺。
服務編排
大部分情況下,微服務之間是相互獨立的,在進行容器調度的時候不需要考慮彼此。
但有時候也會存在一些場景,比如服務 A 調度的前提必須是先有服務 B,這樣的話就要求在進行容器調度的時候,還需要考慮服務之間的依賴關係。
Service Mesh
Service Mesh 的概念最早是由 Buoyant 公司的 CEO William Morgan 提出,他給出的服務網格的定義是:
A service mesh is a dedicated infrastructure layer for handling service-to-service communication. It’s responsible for the reliable delivery of requests through the complex topology of services that comprise a modern, cloud native application. In practice, the service mesh is typically implemented as an array of lightweight network proxies that are deployed alongside application code, without the application needing to be aware.
被很多人定義爲下一代的微服務架構。
目前,Service Mesh 的代表產品當屬 Google 和 IBM 的 lstio。
Istio 的架構可以說由兩部分組成,分別是 Proxy 和 Control Plane。
-
Proxy: 與應用程序部署在同一個主機上,應用程序之間的調用都通過 Proxy 來轉發,目前支持 HTTP/1.1、HTTP/2、gRPC 以及 TCP 請求。
-
Control Plane: 與 Proxy 通信,來實現各種服務治理功能,包括三個基本組件:Pilot、Mixer 以及 Citadel。
參考資料:
- 《從 0 開始學微服務》- 極客時間專欄
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/LYYoO2VgpwhpGMB6FO9_jg