歡迎光臨
每天分享高質量文章

解析微服務架構組件,看這一篇文章就夠

1. 如何發佈和取用服務


服務描述:服務呼叫首先解決的問題就是服務如何對外描述。 常用的服務描述方式包括 RESTful API、XML 配置以及 IDL 檔案三種。


RESTful API


主要被用作 HTTP 或者 HTTPS 協議的接口定義,即使在非微服務架構體系下,也被廣泛採用


  • 優勢:HTTP 協議本身是一個公開的協議,對於服務消費者來說幾乎沒有學習成本,所以比較適合用作跨業務平臺之間的服務協議。

  • 劣勢:性能相對比較低


XML 配置


一般是私有 RPC 框架會選擇 XML 配置這種方式來描述接口,因為私有 RPC 協議的性能比 HTTP 協議高,所以在對性能要求比較高的場景下,採用 XML 配置比較合適。這種方式的服務發佈和取用主要分三個步驟:


  • 服務提供者定義接口,並實現接口

  • 服務提供者行程啟動時,通過加載 server.xml 配置檔案將接口暴露出去。

  • 服務消費者行程啟動時,通過加載 client.xml 配置檔案引入要呼叫的接口。


優勢:


  • 私有 RPC 協議的性能比 HTTP 協議高,所以在對性能要求比較高的場景下,採用 XML 配置方式比較合適 劣勢:

  • 對業務代碼侵入性比較高

  • XML 配置有變更的時候,服務消費者和服務提供者都要更新(建議:公司內部聯繫比較緊密的業務之間採用)


IDL 檔案


IDL 就是接口描述語言(interface description language)的縮寫,通過一種中立的方式來描接口,使得在不同的平臺上運行的物件和不同語言編寫的程式可以相互通信交流。常用的 IDL:一個是 Facebook 開源的 Thrift 協議,另一個是 Google 開源的 gRPC 協議。無論是 Thrift 協議還是 gRPC 協議,他們的工作原來都是類似的。


優勢:用作跨語言平臺的服務之間的呼叫


劣勢:在描述接口定義時,IDL 檔案需要對接口傳回值進行詳細定義。如果接口傳回值的欄位比較多,並且經常變化時,採用 IDL 檔案方式的接口定義就不太合適了。


  • 一方面會造成 IDL 檔案過大難以維護

  • 另一方面只要 IDL 檔案中定義的接口傳回值有變更,都需要同步所有的服務消費者都更新,管理成本太高了。


總結


具體採用哪種服務描述方式是根據實際情況決定,通常情況下, 如果只是企業內部之間的服務呼叫,並且都是 Java 語言的話,選擇 XML 配置方式是最簡單的。如果企業內部存在多個服務,並且服務採用的是不同語言平臺,建議使用 IDL 檔案方式進行描述服務。如果還存在對外開放服務呼叫的情形的話,使用 RESTful API 方式則更加通用。


2. 如何註冊和發現服務


註冊中心原理


在微服務架構下, 主要有三種角色:服務提供者(RPC Server)、服務消費者(RPC Client)和服務註冊中心(Registry),三者的交互關係如圖


  • RPC Server 提供服務,在啟動時,根據服務發佈檔案 server.xml 中配置的信息,向 Registry 訂閱服務,把 Registry 傳回的服務節點串列快取在本地記憶體中,並於 RPC Server 建立連接。

  • RPC Client 呼叫服務,在啟動時,根據服務取用檔案 client.xml 中配置的信息,向 Registry 訂閱服務,把 Registry 傳回的服務節點串列快取在本地記憶體中,並於 RPC Client 建立連接。

  • 當 RPC Server 節點發生變更時,Registry 會同步變更,RPC Client 感知後會掃清本地記憶體中快取的服務節點串列。

  • RPC Client 從本地快取的服務節點串列中,基於負載均衡演算法選擇一臺 RPC Server 發起呼叫。


註冊中心實現方式


註冊中心API


  • 服務註冊接口:服務提供者通過呼叫註冊接口來完成服務註冊

  • 服務反註冊接口:服務提供者通過呼叫服務反註冊接口來完成服務註銷

  • 心跳彙報接口:服務提供者通過呼叫心跳彙報接口完成節點存貨狀態上報

  • 服務訂閱接口:服務消費者呼叫服務訂閱接口完成服務訂閱,獲取可用的服務提供者節點串列

  • 服務變更查詢接口:服務消費者通過呼叫服務變更查詢接口,獲取最新的可用服務節點串列

  • 服務查詢接口:查詢註冊中心當前住了哪些服務信息

  • 服務修改接口:修改註冊中心某一服務的信息


集群部署


註冊中心一般都是採用集群部署來保證高可用性,並通過分佈式一致性協議來確保集群中不同節點之間的資料保持一致。


  • Zookeeper 的工作原理:

    • 每個 Server 在記憶體中儲存了一份資料,Client 的讀請求可以請求任意一個 Server

    • Zookeeper 啟動時,將從實體中選舉一個 leader(Paxos 協議)

    • Leader 負責處理資料更新等操作(ZAB 協議)

    • 一個更新操作方式,Zookeeper 保證了高可用性以及資料一致性


目錄儲存


  • ZooKeeper作為註冊中心儲存服務信息一般採用層次化的目錄結構:

    • 每個目錄在 ZooKeeper 中叫作 znode,並且其有一個唯一的路徑標識

    • znode 可以包含資料和子 znode。

    • znode 中的資料可以有多個版本,比如某一個 znode 下存有多個資料版本,那麼查詢這個路徑下的資料需帶上版本信息。

服務健康狀態檢測


  • 註冊中心除了要支持最基本的服務註冊和服務訂閱功能以外,還必須具備對服務提供者節點的健康狀態檢測功能,這樣才能保證註冊中心裡儲存的服務節點都是可用的。

  • 基於 ZooKeeper 客戶端和服務端的長連接和會話超時控制機制,來實現服務健康狀態檢測的。

  • 在 ZooKeeper 中,客戶端和服務端建立連接後,會話也也隨之建立,並生成一個全域性唯一的 Session ID。服務端和客戶端維持的是一個長連接,在 SESSION_TIMEOUT 周期內,服務端會檢測與客戶端的鏈路是否正常,具體方式是通過客戶端定時向服務端發送心跳訊息(ping 訊息),服務器重置下次 SESSION_TIMEOUT 時間。如果超過 Session 就已經結束了,ZooKeeper 就會認為這個 Session 就已經結束了,ZooKeeper 就會認為這個服務節點已經不可用,將會從註冊中心中刪除其信息。


服務狀態變更通知


  • 一旦註冊中心探測到有服務器提供者節點新加入或者被剔除,就必須立刻通知所有訂閱該服務的服務消費者,掃清本地快取的服務節點信息,確保服務呼叫不會請求不可用的服務提供者節點。

  • 基於 Zookeeper 的 Watcher 機制,來實現服務狀態變更通知給服務消費者的。服務消費者在呼叫 Zookeeper 的 getData 方式訂閱服務時,還可以通過監聽器 Watcher 的 process 方法獲取服務的變更,然後呼叫 getData 方法來獲取變更後的資料,掃清本地混存的服務節點信息。


白名單機制


  • 註冊中心可以提供一個白名單機制,只有添加到註冊中心白名單內的 RPC Server,才能夠呼叫註冊中心的註冊接口,這樣的話可以避免測試環境中的節點意外跑到線上環境中去。


總結


註冊中心可以說是實現服務話的關鍵,因為服務話之後,服務提供者和服務消費者不在同一個行程中運行,實現瞭解耦,這就需要一個紐帶去連接服務提供者和服務消費者,而註冊中心就正好承擔了這一角色。此外,服務提供者可以任意伸縮即增加節點或者減少節點,通過服務健康狀態檢測,註冊中心可以保持最新的服務節點信息,並將變化通知給訂閱服務的服務消費者。


註冊中心一般採用分佈式集群部署,來保證高可用性,並且為了實現異地多活,有的註冊中心還採用多 IDC 部署,這就對資料一致性產生了很高的要求,這些都是註冊中心在實現時必須要解決的問題。


3. 如何實現RPC遠程服務呼叫


客戶端和服務端如何建立網絡連接


HTTP 通信


HTTP 通信是基於應用層HTTP 協議的,而 HTTP 協議又是基於傳輸層 TCP 協議的。一次 HTTP 通信過程就是發起一次 HTTP 呼叫,而一次 HTTP 呼叫就會建立一個 TCP 連接,經歷一次下圖所示的 “三次握手”的過程來建立連接。


完成請求後,再經歷一次“四次揮手”的過程來斷開連接。


Socket 通信


Socket 通信是基於 TCP/IP 協議的封裝,建立一次Socket 連接至少需要一對套接字,其中一個運行於客戶端,稱為 ClientSocket ;另一個運行於服務器端,稱為 ServerSocket 。


  • 服務器監聽:ServerSocket 通過點用 bind() 函式系結某個具體端口,然後呼叫 listen() 函式實時監控網絡狀態,等待客戶端的連接請求。

  • 客戶端請求:ClientSocket 呼叫 connect() 函式向 ServerSocket 系結的地址和端口發起連接請求。

  • 服務端連接確認:當 ServerSocket 監聽都或者接收到 ClientSocket 的連接請求時,呼叫 accept() 函式響應 ClientSocket 的請求,同客戶端建立連接。

  • 資料傳輸:當 ClientSocket 和 ServerSocket 建立連接後,ClientSocket 呼叫 send() 函式,ServerSocket 呼叫 receive() 函式,ServerSocket 處理完請求後,呼叫 send() 函式,ClientSocket 呼叫 receive() 函式,就可以得到傳回結果。

當客戶端和服務端建立網絡連接後,就可以起發起請求了。但網絡不一定總是可靠的,經常會遇到網絡閃斷、連接超時、服務端宕機等各種異常,通常的處理手段有兩種:


  • 鏈路存活檢測:客戶端需要定時地發送心跳檢測小心(一般通過 ping 請求) 給服務端,如果服務端連續 n 次心跳檢測或者超過規定的時間沒有回覆訊息,則認為此時鏈路已經失效,這個時候客戶端就需要重新與服務端建立連接。

  • 斷連重試:通常有多種情況會導致連接斷開,比如客戶端主動關閉、服務端宕機或者網絡故障等。這個時候客戶端就需要與服務端重新建立連接,但一般不能立刻完成重連,而是要等待固定的間隔後再發起重連,避免服務端的連接回收不及時,而客戶端瞬間重連的請求太多而把服務端的連接數占滿。


服務端如何處理請求


同步阻塞方式(BIO)


  • 客戶端每發一次請求,服務端就生成一個執行緒去處理。當客戶端同時發起的請求很多事,服務端需要創建很多的執行緒去處理每一個請求,如果達到了系統最大的執行緒數瓶頸,新來的請求就沒法處理了。

  • BIO 適用於連接數比較小的業務場景,這樣的話不至於系統中沒有可用執行緒去處理請求。這種方式寫的程式也比較簡單直觀,易於理解。


同步非阻塞(NIO)


  • 客戶端每發一次請求,服務端並不是每次都創建一個新執行緒來處理,而是通過 I/O 多路復用技術進行處理。就是把國歌 I/O 的阻塞復用到聽一個 select 的阻塞上,從而使系統在單執行緒的情況下可以同時處理多個客戶端請求。這種方式的優勢是開銷小,不用為每個請求創建一個執行緒,可以節省系統開銷。

  • NIO 適用於連接數比較多並且請求消耗比較輕的業務場景,比如聊天服務器。這種方式相比 BIO,相對來說編程比較複雜。


異步非阻塞(AIO)


  • 客戶端只需要發起一個 I/O 操作然後立即傳回,等 I/O 操作真正完成以後,客戶端會得到 I/O 操作完成的通知,此時客戶端只需要對資料進行處理就好了,不需要進行實際的 I/O 讀寫操作,因為真正的 I/O 讀取或者寫入操作已經由內核完成了。這種方式的優勢是客戶端無需等待,不存在阻塞等待問題。

  • AIO 適用於連接數比較多而且請求消耗比較重的業務場景,比如涉及 I/O 操作的相冊服務器。這種方式相比另外兩種,編程難難度最大,程式也不易於理解。


建議


最為穩妥的方式是使用成熟的開源方案,比如 Netty、MINA 等,它們都是經過業界大規模應用後,被充分論證是很可靠的方案。


資料傳輸採用什麼協議


無論是開放的還是私有的協議,都必須定義一個“契約”,以便服務消費和服務提供者之間能夠達成共識。服務消費者按照契約,對傳輸的資料進行編碼,然後通過網絡傳輸過去;服務提供者從網絡上接收到資料後,按照契約,對傳輸的資料進行解碼,然後處理請求,再把處理後的結果進行編碼,通過網絡傳輸傳回給服務消費者;服務消費者再對傳回的結果進行解碼,最終得到服務提供者處理後的傳回值。


HTTP 協議


  • 訊息頭

    • Server 代表是服務端服務器型別

    • Content-Length 代表傳回資料的長度

    • Content-Type 代表傳回資料的型別

  • 訊息體

    • 具體的傳回結果

資料該如何序列化和反序列化


一般資料在網絡中進行傳輸,都要先在發送方一段對資料進行編碼,經過網絡傳輸到達另一段後,再對資料進行解碼,這個過程就是序列化和反序列化


常用的序列化方式分為兩類:文本類如 XML/JSON 等,二進制類如 PB/Thrift 等,而具體採用哪種序列化方式,主要取決於三個方面的因素。


  • 支持資料結構型別的豐富度。資料結構種類支持的越多越好,這樣的話對於使用者來說在編程時更加友好,有些序列化框架如 Hessian 2.0 還支持複雜的資料結構比如 Map、List等。

  • 跨語言支持。

  • 性能。主要看兩點,一個是序列化後的壓縮比,一個是序列化的速度。以常用的 PB 序列化和 JSON 序列化協議為例來對比分析,PB 序列化的壓縮比和速度都要比 JSON 序列化高很多,所以對性能和儲存空間要求比較高的系統選用 PB 序列化更合;而 JSON 序列化雖然性能要差一些,但可讀性更好,所以對性能和儲存空間要求比較高的系統選用 PB 序列化更合適對外部提供服務。


總結


  • 通信框架:它主要解決客戶端和服務端如何建立連接、管理連接以及服務端如何處理請求的問題。

  • 通信協議:它主要解決客戶端和服務端採用哪些資料傳輸協議的問題。

  • 序列化和反序列化:它主要解決客戶端和服務端採用哪種資料編碼的問題。


這三部分就組成了一個完成的RPC 呼叫框架,通信框架提供了基礎的通信能力,通信協議描述了通信契約,而序列化和反序列化則用於資料的編/解碼。一個通信框架可以適配多種通信協議,也可以採用多種序列化和反序列化的格式,比如服務話框架 不僅支持 Dubbo 協議,還支持 RMI 協議、HTTP 協議等,而且還支持多種序列化和反序列化格式,比如 JSON、Hession 2.0 以及 Java 序列化等。


4. 如何監控微服務呼叫


在談論監控微服務監控呼叫前,首先要搞清楚三個問題:監控的物件是什麼?具體監控哪些指標?從哪些維度進行監控?


監控物件


  • 用戶端監控:通常是指業務直接對用戶提供的功能的監控。

  • 接口監控:通常是指業務提供的功能所以來的具體 RPC 接口監控。

  • 資源監控:通常是指某個接口依賴的資源的監控。(eg:Redis 來儲存關註串列,對 Redis 的監控就屬於資源監控。)

  • 基礎監控:通常是指對服務器本身的健康狀況的監控。(eg: CPU、MEM、I/O、網卡帶寬等)


監控指標


  • 請求量

    • 實時請求量(QPS Queries Per Second):即每秒查詢次數來衡量,反映了服務呼叫的實時變化情況

    • 統計請求量(PV Page View):即一段時間內用戶的訪問量來衡量,eg:一天的 PV 代表了服務一天的請求量,通常用來統計報表

  • 響應時間:大多數情況下,可以用一段時間內所有呼叫的平均耗時來反應請求的響應時間。但它只代表了請求的平均快慢情況,有時候我們更關心慢請求的數量。為此需要把響應時間劃分為多個區間,比如0~10ms、10ms~50ms、50ms~100ms、100ms~500ms、500ms 以上這五個區間,其中 500ms 以上這個區間內的請求數就代表了慢請求量,正常情況下,這個區間內的請求數應該接近於 0;在出現問題時,這個區間內的請求數應該接近於 0;在出現問題時,這個區間內的請求數會大幅增加,可能平均耗時並不能反映出這一變化。除此之外,還可以從P90、P95、P99、P999 角度來監控請求的響應時間在 500ms 以內,它代表了請求的服務質量,即 SLA。

  • 錯誤率:通常用一段時間內呼叫失敗的次數占呼叫總次數的比率來衡量,比如對於接口的錯誤率一般用接口傳回錯誤碼為 503 的比率來表示。


監控維度


  • 全域性維度:從整體角度監控物件的請求量、平均耗時以及錯誤率,全域性維度的監控一般是為了讓你對監控物件的呼叫情況有個整體瞭解。

  • 分機房維度:為了業務高可用,服務部署不止一個機房,因為不同機房地域的不同,同一個監控物件的各種指標可能會相差很大。

  • 單機維度:同一個機房內部,可能由於採購年份和批次不的不同,各種指標也不一樣。

  • 時間維度:同一個監控物件,在每天的同一時刻各種指標通常也不會一樣,這種差異要麼是由業務變更導致,要麼是運營活動導致。為了瞭解監控物件各種指標的變化,通常需要與一天前、一周前、一個月前,甚至三個月前比較。

  • 核心維度:業務上一般會依據重要性成都對監控物件進行分級,最簡單的是分成核心業務和非核心業務。核心業務和非核心業務在部署上必須隔離,分開監控,這樣才能對核心業務做重點保障。


對於一個微服務來說,必須要明確監控哪些物件、哪些指標,並且還要從不同的維度進行監控,才能掌握微服務的呼叫情況。


監控系統原理


  • 資料採集:收集到每一次呼叫的詳細信息,包括呼叫的響應時間、呼叫是否成功、呼叫的發起者和接收者分別是誰,這個過程叫做資料採集。

  • 資料傳輸:採集到資料之後,要把資料通過一定的方式傳輸給資料處理中心進行處理,這個過程叫做資料出傳輸。

  • 資料處理:資料傳輸過來後,資料處理中心再按照服務的維度進行聚合,計算出不同服務的請求量、響應時間以及錯誤率等信息並儲存起來,這個過程叫做資料處理。

  • 資料展示:通過接口或者 DashBoard 的形式對外展示服務的呼叫情況,這個過程叫做資料展示。


資料採集


  • 服務主動上報

  • 代理收集:這種處理方式通過服務呼叫後把呼叫的詳細信息記錄到本地日誌檔案中,然後再通過代理去解析本地日誌檔案,然後再上報服務的呼叫信息。


不管是哪種方式,首先要考慮的問題就是採樣率,也就是採集資料的頻率。一般來說,採樣率越高,監控的實時性就越高,精確度也越高。但採樣對系統本身的性能也會有一定的影響,尤其是採集後的資料需要寫到本地磁盤的時候,過高的採樣率會導致系統寫入的 I/O 過高,進而會影響到正常的服務呼叫。所以合理的採樣率是資料採集的關鍵,最好是可以動態控制採樣率,在系統比較空閑的時候加大採樣率,追求監控的實時性與精確度;在系統負載比較高的時候減少採樣率,追求監控的可用性與系統的穩定性。


資料傳輸


  • UDP傳輸:這種處理方式是資料處理單元提供服務器的請求地址,資料採集後通過 UDP 協議與服務器建立連接,然後把資料發送過去。

  • Kafka傳輸:這種處理方式是資料採集後發送都指定的 Topic,然後資料處理單元再訂閱對應的 Topic,就可以從 Kafka 訊息佇列中讀取對應的資料。


無論哪種傳輸方式,資料格式十分重要,尤其是對帶寬敏感以及解析性能要求比較高的場景,一般資料傳輸時採用的資料格式有兩種:


  • 二進制協議,最常用的就是 PB 物件

  • 文本協議,最常用的就是 JSON 字串


資料處理


  • 接口維度聚合:把實時收到的資料按照呼叫的節點維度聚合在一起,這樣就可以得到每個接口的實時請求、平均耗時等信息。

  • 機器維度聚合:把實時收到的資料按照呼叫的節點維度聚合在一起,這樣就可以從單機維度去查看每個接口的實時請求量、平均耗時等信息。


聚合後的資料需要持久化到資料庫中儲存,所選用的資料庫一般分為兩種:


  • 索引資料庫:比如 Elasticsearcher,以倒排索引的資料結構存書,需要查詢的時候,根據索引來查詢。

  • 時序資料庫:比如 OpenTSDB,以時序序列資料的方式儲存,查詢的時候按照時序如 1min、5min 等維度查詢


資料展示


  • 曲線圖:監控變化趨勢。

  • 餅狀圖:監控占比分佈。

  • 格子圖:主要坐一些細粒度的監控。

總結


  • 服務監控子啊微服務改造過程中的重要性不言而喻,沒有強大的監控能力,改造成微服務架構後,就無法掌控各個不同服務的情況,在遇到呼叫失敗時,如果不能快速發現系統的問題,對於業務來說就是一場災難。

  • 搭建一個服務監控系統,設計資料採集、資料傳輸、資料處理、資料展示等多個環節,每個環節都需要根據自己的業務特點選擇合適的解決方案


5. 如何追蹤微服務呼叫


跟蹤記錄一次用戶請求都發起了哪些呼叫,經過哪些服務處理,並且記錄每一次呼叫所涉及的詳細信息,這時候如果發生呼叫失敗,就可以通過這個日誌快速定位是在哪個環節出了問題。


服務追蹤的作用


優化系統瓶頸


  • 通過記錄呼叫經過的每一條鏈路上的耗時,可以快速定位整個系統的瓶頸點在哪裡。可能出現的原因如下:

    • 運營商網絡延遲

    • 網關係統異常

    • 某個服務異常

    • 快取或者資料庫異常

  • 通過服務追蹤,可以從全域性視角上去觀察,找出整個系統的瓶頸點所在,然後做出針對性的優化


優化鏈路呼叫


  • 通過服務追蹤可以分析呼叫所經過的路徑,然後評估是否合理

  • 一般業務都會在多個資料中心都部署服務,以實現異地容災,這個時候經常會出現一種狀況就是服務 A 呼叫了另外一個資料中心的服務 B,而沒有呼叫同處於一個資料中心的舒服務 B。跨資料中心的呼叫視距離遠近都會有一定的網絡延遲,像北京和廣州這種幾千公里距離的網絡延遲可能達到了30ms以上,這對於有些業務幾乎是不可接受的。通過對呼叫鏈路進行分析,可以找出跨資料中的服務呼叫,從而進行優化,儘量規避這總情況出現。


生成網絡拓撲


  • 通過服務追蹤系統中記錄的鏈路信息,可以生成一張系統的網絡呼叫拓撲圖,它可以反映系統都依賴了哪些服務,以及服務之間的呼叫關係是什麼樣的,可以一目瞭然。除此之外,在網絡拓撲圖上還可以把服務呼叫的詳細信息也標出來,也能起到服務監控的作用。


透明傳輸資料


  • 除了服務追蹤,業務上經常有一種需求,期望能把一些用戶資料,從呼叫的開始一直往下傳遞,以便系統中的各個服務都能獲取到這個信息。比如業務想做一些 A/B 測試,這時候就想通過服務追蹤系統,把 A/B 測試的開關邏輯一直往下傳遞,經過的每一層服務都能獲取到這個開關值,就能夠統一進行 A/B 測試。


服務追蹤原理


服務追蹤鼻祖:Google 發佈的一篇的論文Dapper, [a Large-Scale Distributed Systems Tracing Infrastructure


  • 核心理念:通過一個全域性唯一的 ID 將分佈在各個服務節點上的同一次請求串聯起來,從而還原原有的呼叫關係,可以追蹤系統問題、分析呼叫資料並統計各種系統指標

  • 可以說後面的誕生各種服務追蹤系統都是基於 Dapper 衍生出來的,比較有名的有 Twitter 的Zipkin、阿裡的鷹眼、美團的MTrace等。


講解下服務追蹤系統中幾個最基本概念


  • traceId:用於標識某一次具體的請求ID。

  • spanId:用於標識一次 RPC 呼叫在分佈式請求中的位置。

  • annotation:用於業務自定義埋點資料,可以是業務感興趣的上上傳到後端的資料,比如一次請求的用戶 UID。


traceId 是用於串聯某一次請求在系統中經過的所有路徑,spanId 是用於區分系統不同服務之間呼叫的先後關係,而annotation 是用於業務自定義一些自己感興趣的資料,在上傳 traceId 和 spanId 這些基本信息之外,添加一些自己感興趣的信息。


服務追蹤系統實現


上面是服務追蹤系統架構圖,一個服務追蹤系統可以分三層:


  • 資料採集層:負責資料埋點並上報

  • 資料處理層:負責資料的儲存與計算

  • 資料展示層:負責資料的圖形化展示


資料採集層


作用:在系統的各個不同的模塊中盡心埋點,採集資料並上報給資料處理層進行處理。


  • CS(Client Send)階段 : 客戶端發起請求,並生成呼叫的背景關係。

  • SR(Server Recieve)階段 : 服務端接收請求,並生成背景關係。

  • SS(Server Send)階段 : 服務端傳回請求,這個階段會將服務端背景關係資料上報,下麵這張圖可以說明上報的資料有:traceId=123456,spanId=0.1,appKey=B,method=B.method,start=103,duration=38.

  • CR(Client Recieve)階段 : 客戶端接收傳回結果,這個階段會將客戶端背景關係資料上報,上報的資料有:traceid=123456,spanId=0.1,appKey=A,method=B.method,start=103,duration=38。

資料處理層


作用:把資料上報的資料按需計算,然後落地儲存供查詢使用


  • 實時資料處理:要求計算效率比較高,一般要對收集的鏈路資料能夠在秒級別完成聚合計算,以供實時查詢

    • 針對實時資料處理,一般使用 Storm 或者 Spack Streaming 來對鏈路資料進行實時聚合加工,儲存一拜是用 OLTP 資料倉庫,比如 HBase,使用 traceId 作為 RowKey,能天然地把一條呼叫鏈聚合在一起,提高查詢效率。

  • 離線資料處理:要求計算效率相對沒那麼高,一般能在小時級別完成鏈路資料的聚合計算即可,一般用作彙總統計。

    • 針對離線資料處理,一般通過運行 MapReduce 或者 Spark 批處理程式來對鏈路資料進行離線計算,儲存一般使用 Hive


資料展示


作用:將處理後的鏈路信息以圖形化的方式展示給用戶和做故障定位


  • 呼叫鏈路圖(eg:Zipkin)

    • 服務整體情況:服務總耗時、服務呼叫的網絡深度、每一層經過的系統,以及多少次呼叫。下圖展示的一次呼叫,總耗時 209.323ms,經過了 5 個不同系統模塊,呼叫深度為 7 層,共發生了 2



  • 呼叫拓撲圖(Pinpoint)

    • 呼叫拓撲圖是一種全域性視野,在實際專案中,主要用作全域性監控,用戶發現系統異常的點,從而快速做出決策。比如,某一個服務突然出現異常,那麼在呼叫鏈路拓撲圖中可以看出對這個服務的呼叫耗時都變高了,可以用紅色的圖樣標出來,用作監控報警。

總結


  • 服務追蹤能夠幫助查詢一次用戶請求在系統中的具體執行路徑,以及每一條路徑下的上下游的詳細情況,對於追查問題十分有用。

  • 實現一個服務追蹤系統,設計資料採集、資料處理和資料展示三個流程,有多種實現方式,具體採取某一種要根據自己的業務情況來選擇。


6. 微服務治理的手段有哪些


一次服務呼叫,服務提供者、註冊中心、網絡這三者都可能會有問題,此時服務消費者應該如何處理才能確保呼叫成功呢?這就是服務治理要解決的問題。


節點管理


  • 服務呼叫失敗一般是由兩類原因引起的

    • 服務提供者自身出現問題,比如服務器宕機、行程意外退出等

    • 網絡問題,如服務提供者、註冊中心、服務消費者這三者任意兩者之間的網絡問題


無論是服務哪種原因,都有兩種節點管理手段:


註冊中心主動摘除機制


這種機制要求服務提供者定時的主動向註冊中心彙報心跳,註冊中心根據服務提供者節點最近一次彙報心跳的時間與上一次彙報心跳時間做比較,如果超出一定時間,就認為服務提供者出現問題,繼而把節點從服務串列中摘除,並把最近的可用服務節點串列推送給服務消費者。


服務消費者摘除機制


雖然註冊中心主動摘除機制可以解決服務提供者節點異常的問題,但如果是因為註冊中心與服務提供者之間的網絡出現異常,最壞的情況是註冊中心會把服務節點全部摘除,導致服務消費者沒有可能的服務節點呼叫,但其實這時候提供者本身是正常的。所以,將存活探測機制用在服務消費者這一端更合理,如果服務消費者呼叫服務提供者節點失敗,就將這個節點從記憶體儲存的可用夫提供者節點串列一處。


負載均衡演算法


常用的負載均衡演算法主要包括以下幾種:


  • 隨機演算法(均勻)

  • 輪詢演算法(按照固定的權重,對可用服務節點進行輪詢)

  • 最少活躍呼叫演算法(性能理論最優)

  • 一致性 Hash 演算法(相同引數的請求總是發到同一服務節點)


服務路由


  • 對於服務消費者而言,在記憶體中的可用服務節點串列中選擇哪個節點不僅由負載均衡演算法決定,還由路由規則決定。

  • 所謂的路由規則,就是通過一定的規則如條件運算式或者正則運算式來限定服務節點的選擇範圍。


為什麼要指定路由規則呢?主要有兩個原因:


  • 業務存在灰度發佈的需求

    • 比如,服務提供者做了功能變更,但希望先只讓部分人群使用,然後根據這部分人群的使用反饋,再來決定是否全量發佈。

  • 多機房就近訪問的需求

    • 跨資料中心的呼叫視距離遠近都會有一定的網絡延遲,像北京和廣州這種幾千公里距離的網絡延遲可能達到了30ms以上,這對於有些業務幾乎是不可接受的,所以就要一次服務呼叫儘量選擇同一個 IDC 內部節點,從而減少網絡耗時開銷,提高性能。這時一般可以通過 IP 段規則來控制訪問,在選擇服務節點時,優先選擇同一 IP 段的節點。


那麼路由規則該如何配置?


  • 靜態配置:服務消費者本地存放呼叫的路由規則,如果改變,需重新上線才能生效

  • 動態配置:路由規則存放在配置中心,服務消費者定期去請求註冊中心來保持同步,要想改變消費者的路由配置,可以通過修改註冊中心的配置,服務消費者在下一個同步周期之後,就會請求註冊中心更新配置,從而實現動態變更


服務容錯


常用的手段主要有以下幾種:


  • FailOver:失敗自動切換(呼叫失敗或者超時,可以設置重試次數)

  • FailBack:失敗通知(呼叫失敗或者超時,不立即發起重試,而是根據失敗的詳細信息,來決定後續的執行策略)

  • FailCache:失敗快取(呼叫失敗或者超時,不立即發起重試,而是隔一段時間後再次嘗試發起呼叫)

  • FailFirst:快速失敗(呼叫一次失敗後,不再充實,一般非核心業務的呼叫,會採取快速失敗策略,呼叫失敗後一般就記錄下失敗日誌就傳回了)


一般對於冪等的呼叫可以選擇 FailOver 或者 FailCache,非冪等的呼叫可以選擇 Failback 或者 FailFast


總結


  • 節點管理是從服務節點健康狀態角度來考慮,負載均衡和服務路由是從服務節點訪問優先級角度來考慮,而服務容錯是從呼叫的健康狀態來考慮,可謂殊途同歸。

  • 在實際的微服務架構中,上面的服務治理手段一般都會在服務框架中預設即成,比如 阿裡的 Dubbo、微博開源的服務架構 Motan等。


原文鏈接:https://my.oschina.net/rosetta/blog/2051691

溫馨提示:

請搜索“ICT_Architect”“掃一掃”二維碼關註公眾號,點擊原文鏈接獲取更多電子書詳情

求知若渴, 虛心若愚

赞(0)

分享創造快樂