微服務系列 2:微服務化框架的模型和治理能力設計

張哥說技術發表於2023-01-03

緊接上一篇,微服務系列 1:微服務化框架落地的挑戰和核心需求,那麼基於這些核心訴求,我們整個的微服務框架的模型是如何?又該具備哪些核心的治理能力呢?透過本文來一一知曉!

微服務系列 2:微服務化框架的模型和治理能力設計

一、服務治理的理解

隨著網際網路的發展和容器化的發展,更進一步的推動了微服務化的建設,在微服務體系下,我們的服務治理,首先要做的就是針對我們大量的服務怎麼更好的進行管理,保證我們系統在執行過程中能夠自動化的發現問題並自動解決一些問題,從而使我們的系統更加的穩定。而這些治理的策略,至少要包括服務的限流、降級、容錯,以及服務的彈性伸縮、灰度釋出,還有自動化的運維。

服務治理與管控包含幾大塊:服務的治理、服務的監控、服務的可用性保障

  1. 1. 服務的治理:服務提供方的管理、服務使用方的管理、服務依賴的管理、服務的呼叫管理、服務的註冊和發現、服務的部署和升級、服務的版本化管理

  2. 2. 服務的監控:資料採集、資料處理、鏈路跟蹤、白盒化的報表展示、系統級的監控

  3. 3. 服務的可用性:容錯(Failover、Failfast)、負載均衡、過載保護、服務降級、頻率限制

服務化框架的核心能力,括:RPC、服務發現與註冊、負載均衡、容錯、熔斷、限流、降級、許可權、全鏈路日誌跟蹤也是屬於服務治理的範疇之內,當然服務治理不僅僅包含這些核心能力,還包括各種管理,框架要做的就是全套服務,使用只管放心接入使用,所有治理相關的都由框架去實現。

二、微服務架構模型

在我前面一篇文章《微服務化框架落地的挑戰和核心需求》中,我梳理了微服務化框架落地的一些挑戰和核心需求,那麼針對這些核心需求,我們看看微服務要實現那些核心能力,也就是將上述需求進行實現,實現的時候基於現有的技術方案來進行抽象和統一,最終就可以形成我們的微服務框架。我將我理解的微服務架構模型分為如下三部分:

  • • 核心能力,這個是框架必須要實現的,而且是任何一個服務化框架必備的能力

  • • 擴充套件能力,這個是可以透過框架的外掛化進行擴充套件的支援,當然,框架本身也可以支援,但是從我個人的理解上來看,這個透過外部元件進行擴充套件,會更加契合整體的設計

  • • 管理平臺,這個是圍繞整個服務化框架、業務系統、開發流程所需要依賴的一些基礎平臺,這些平臺可以更好的幫助我們進行服務的釋出和上線,以及上線後的管理

微服務系列 2:微服務化框架的模型和治理能力設計

三、微服務框架的治理能力設計

3-1、微服務框架的核心能力

微服務需要提供的核心能力包括:微服務架構模型中通訊的基礎協議RPC、服務發現與註冊、負載均衡、容錯、熔斷、限流、降級、彈性擴縮容等

服務間通訊:RPC

在微服務架構中,服務之間的通訊,一般都是透過 遠端訪問 的方式來進行。遠端訪問的通訊方式有很多中,包括:

  • • 從通訊風格上, 一般有 REST,RPC 和 定製協議這三種方式

  • • 從互動方式上,按照互動物件的數量可以分為一對一和一對多,按照應答返回的方式分為同步和非同步。

RPC 是服務通訊的基礎,如果沒有統一的 RPC 框架,各個團隊就需要實現自己的一套介面協議定義、序列化、反序列化、網路框架、服務治理等重複工作,因此可以說,微服務的核心就是要有一個統一的 RPC 框架,統一一套 RPC 是微服務化首先要解決的問題。透過統一的 RPC 框架協議,可以統一我們的介面定義,減少接入和學習的成本。同時,如果我們是基於已有的框架來實現,那麼需要儘可能的保證相容原有協議(比如基於 gRPC 的話,就要儘量保證相容 gRPC 的介面協議)

業內可選的 RPC 框架有很多,比如 dubbo/dubbox,motan,thrift,grpc,Karyon/Ribbon等,在我之前的公司,我們推行服務化框架的時候,是選擇了 gRPC 作為我們的基礎框架,然後基於 gRPC 豐富了很多服務治理的策略,整體線上執行良好,並且有較多業務接入。

服務註冊與發現

微服務架構下,我們的業務都是由一系列獨立的微服務組成,那麼這些微服務之間如何才能發現彼此?這就要求微服務之間存在一種發現的機制。這個機制,叫做服務發現,服務發現的核心功能就是服務登錄檔,登錄檔記錄了所有的服務節點的配置和狀態,每個微服務啟動後都需要將自己的資訊註冊到服務登錄檔,然後由微服務或者負載均衡系統到服務登錄檔查詢可用服務。

服務端發現和客戶端發現模式

在設計上,服務發現機制的設計有服務端發現和客戶端發現兩種實現方式。

  • • 服務端發現模式(server-side):可以透過 DNS 或者帶 VIP 的負載均衡來實現。

    • • 優點是對客戶端無侵入性,客戶端只需要簡單的向負載均衡或者服務域名發起請求,無需關係服務發現的具體細節,也不用引入服務發現的邏輯

    • • 缺點是不靈活,不方便難異化處理;並且同時需要引入一個統一的負載均衡器。

  • • 客戶端發現模式(client-side):需要客戶端服務註冊中心中查詢服務地址列表,然後再決定透過哪個地址請求服務。

    • • 靈活性更高,可以根據客戶端的訴求進行滿足自身業務的負載均衡,但是客戶端需要引入服務發現的邏輯,同時依賴服務註冊中心

    • • 常見服務註冊元件包括:zookeeper、etcd、Consul

在我曾經的專案中,我們是透過客戶端發現模式來設計的,我們會把這個能力整合在微服務框架裡面,然後微服務框架在啟動的時候,會將自己服務的資訊註冊到註冊中心,註冊中心我們當時選用的是 ETCD。

服務註冊和分析的一致性要求

服務註冊和服務發現,在實現時根據對一致性要求的不同,分成兩個流派:

  1. 1. 強一致性

  • • 比較常見的分散式一致性協議是 PAXOS 協議和 Raft 協議。相比 PAXOS 而言,Raft 協議易於理解和實現,因此最新的分散式一致性方案大都選擇 Raft 協議。

  • • zookeeper 採用的是 PAXOS 協議,consul 和 etcd 採用的是 Raft 協議。

  • 2. 弱一致性

    • • 如果對一致性要求不高,可以選擇以 DNS 為基礎的方案。弱一致性方案比較少,一般多用於 REST 或者 HTTP + json / web service 等簡單場合

    雖然方案眾多,但是對於普通使用者的大多數場景而言,一般網際網路公司建議從 zookeeper、etcd、consul 這三個主流方案中選擇,我之前的設計裡面,我們選擇的就是 etcd。

    服務負載均衡

    路由演算法

    服務負載均衡,一般情況下,是會和服務發現放在一起設計的,因為服務發現後,緊接著,就是要對服務進行路由,也就是服務的負載均衡,他們一般是作為一個整體系統來設計。雖然服務發現機制有服務端發現和客戶端發現兩種實現方式,但是無論放在哪裡實現,針對服務進行路由,也就是負載均衡的核心的功能就是路由演算法。常見的路由演算法有:隨機路由、輪詢路由、最小壓力路由、最小連線數路由等。

    目前微服務框架中,大都是在客戶端發現模式(client-side) 來實現服務路和負載均衡,一般也都會支援常見的負載均衡策略,如隨機,輪訓,hash,權重,連線數【連線數越少,優先順序越高】。

    心跳檢測與摘除

    我們要對服務進行負載均衡的話,有一個先決條件,就是我們要路由的服務,一定是可用的,怎麼保證服務可用,那麼就需要進行心跳檢測與摘除:進行心跳檢測,每個 N 秒檢測一次,超過 M 次心跳連不上就進行摘除。這樣的話,就可以保證我們路由的服務都是正常的。

    多機房路由

    如果服務部署在多個不同的機房,那麼我們路由的時候,一般的策略就是優先呼叫本地機房,失敗重試也是本機房呼叫,如果本地機房沒有可用節點則路由到其他機房。同時可以增加一個開關控制是否 failover 到遠端機房。

    服務容錯

    負載均衡和容錯是服務高可用的重要手段。服務容錯的設計在業內有個基本原則,就是“Design for Failure”。常見的服務容錯策略如請求重試、限流、降級、熔斷等策略

    超時與重試

    超時是一種最常見的服務容錯模式,只要涉及到網路呼叫,那麼就有可能因為網路問題、依賴服務問題,導致呼叫的時候出現請求超時,一般情況下,我們都會對每個請求設定好超時時間(一般介面不應該超過 1s),如果超時未返回,那麼就會斷開連線,從而可以做對應的處理,比如返回失敗、釋放連線、釋放資源等。如果不做超時處理,那麼可能會導致請求一直卡在,然後一直佔用系統資源,從而使得整個系統資源耗盡,或者使用者一直刷不到資料。

    重試,是指下游出錯(不管是真的出錯,還是超時)的時候進行使用,透過重試可以較好的保證資料的可靠性,尤其是當下遊有網路波動導致超時的場景下,會比較有效。重試的設計,有幾個要點:

    • • 重試的次數,一定要有一個最大值,經過一定的重試次數,還是拿不到正確的資料,那麼就應該返回錯誤,而不能一直重試,

    • • 重試的時間間隔,需要設定,不能不停的重試,避免因為重試過多、過快導致系統負擔增大,重試的時間策略,我們一般都會引入**Exponential Backoff 的策略,也就是所謂的 "指數級退避"**。在這種情況下,每一次重試所需要的 sleep 時間都會指數增加。這其實和 TCP 的擁塞控制有點像。

    服務限流

    限流和降級都是用來保證核心服務穩定性的有效策略。限流是指限制每個服務(或者介面)的最大訪問量,超過這個閾值就會被拒絕,是從使用者訪問的請求量量去考慮問題;降級是指高峰期對非核心的系統進行降級從而保證核心服務的可用性,是從系統功能的優先順序角度考慮問題。

    限流的目的是為了保證我們的系統不過載,當系統流量增大的時候,透過限流,可以保證能夠在系統可承受的壓力之下穩定執行,否則,一旦過載則會導致整個系統不可用。在我們實際應用中,到處都可以看到限流的一些策略場景,比如 Nginx 的 limit_conn 模組用來限制最大併發連線數、limit_req 模組用來限制每秒平均速率。

    常見的限流方式可以分為兩類:基於請求限流和基於資源限流。

    • • 基於請求限流。從外部系統的訪問請求量的角度來考慮限流,常見的方式有:限制總量、限制時間量。

    • • 基於資源限流。從內部系統的使用資源的角度來考慮限流,找到系統內部影響效能的關鍵資源,對其使用上限進行限制。常見的內部資源有:連線數、檔案控制程式碼、執行緒數、請求佇列等。

    限流的維度和級別主要可以分為以下幾類:服務級別維度的限流、介面級別維度的限流、業務細粒度級別維度的限流。

    • • 服務級別維度的限流、介面級別維度的限流主要目的是保護我們的系統,防止服務過載。

    • • 業務細粒度級別維度的限流。主要目的是防刷,也是防止單使用者或佔用過多處理能力,影響其他使用者。就是我們根據業務內部的具體維度去限流,比如我們可以透過 APPID 和 UID 這兩個常見的業務維度。appid 就是對應某一類請求或者一個業務型別,UID 就是對應一個唯一的使用者。

    服務降級

    首先,降級之前,一定是要先限流,限流之後,系統還是可能存在問題,才考慮降級。同時,降級不僅僅是開發人員的事情,還需要和產品人員溝通和協商降級後的處理以及對應的效果。

    降級設計(Degradation)本質是為了解決資源不足和訪問量過大的問題,當資源和訪問量出現矛盾的時候,在有限的資源下,為了能夠扛住大量的請求,我們就需要對系統進行降級操作。降級是指我們劃分好系統的核心功能和非核心功能,然後當我們的系統超過最大處理能力之後,直接關閉掉非核心的功能,從而保障核心功能的可用。關閉掉非核心的功能後可以使我們的系統釋放部分資源,從而可以有資源來處理核心功能。

    熔斷設計

    當下遊服務出現異常的時候(包括出錯、超時),一般我們會進行短時間且少量的重試操作,但是如果重試幾次依然無法解決,那麼我們也不能一直重試,這樣會給下游帶來更大的壓力。

    熔斷,就是在重試、限流等策略執行之後,還是無法解決的情況下的另外一種保護系統的策略手段,透過熔斷,可以較好的保護好下游服務。熔斷最重要的價值在於限制故障影響範圍,透過熔斷減少或拒絕請求從而有利於下游系統的恢復。

    熔斷系統設計的思路就是圍繞熔斷的三個狀態來設計,一般我們透過三個模組(異常請求計算模組、熔斷探測恢復模組、熔斷記錄和報警模組)來設計,主要的流程就是,在 Close 狀態下,當我們的請求失敗 N 次後,在 X 時間不再繼續請求,實現熔斷,進入 Half-Open 狀態;與此同時,在 X 時間後恢復 M% 的請求,如果 M% 的請求都成功,則恢復到正常狀態,進入 close 狀態,否則再熔斷 Y 時間,依此迴圈。如果一直無法恢復,那麼可以直接進入 Open 狀態。

    熔斷和降級這兩個策略,看著比較像,字面的意思上來看都是要快速拒絕掉請求。但是他們是兩個維度的設計,降級的目的是應對系統自身的故障,而熔斷的目的是應對我們系統依賴的外部服務故障的情況。

    叢集容錯

    微服務化後,服務可能會部署到多個不同的機房,也就是可能會部署到多個不同的叢集上,為此,就有叢集容錯一說,叢集容錯裡面,我們常見的兩個策略是

    • • failfast 策略,快速失敗,只發起一次呼叫,失敗立即報錯。設計的時候需要注意

      • • 控制超時時間的判斷和記錄:連線超時、響應超時

    • • failover 策略,失敗自動切換,當出現失敗,重試到其他叢集的服務

      • • 優先呼叫本地機房,失敗也是本機房呼叫

      • • 預設不 failover 到遠端機房,開關開啟的時候才啟用一定比例的 failover 策略呼叫到遠端機房

      • • 需要根據錯誤碼來區分不同的響應資訊決定是否重試,比如404 400等情況是不需要重試的

    彈性擴縮容

    彈性擴縮容就要求應用服務設計為無狀態模型,一般微服務框架無法實現彈性擴縮容,還需要配合其他的技術如容器技術(Kubernetes)、雲服務技術等,這樣才能實現彈性。

    這裡更多的是結合其他能力比如 K8s,特意單獨提出來,是為了說明這個的重要性。

    3-2、微服務框架的擴充套件能力

    API 接入層閘道器

    微服務需要一個統一的 API 閘道器,負責外部系統的訪問操作。API 閘道器是外部系統訪問的入口,所有的外部系統接⼊內部系統都需要透過 API 閘道器,我們一般的後端服務,外網是無法直接訪問的,API 需要做一層處理,主要包括接入鑑權(是否允許接入)、許可權控制(可以訪問哪些功能)、傳輸加密、請求路由、流量控制等功能。一切 ok 後才能訪問到我們的內網服務。

    監控和告警

    監控系統的開源代表作:Prometheus + Grafana,遵循 OpenMetrics 規範,基本資料格式分為 Gauge、Count、Summary、Histogram。在我們之前的實現中,公司也是採用普羅米修斯【prometheus】+ dashboard(grafana) 來實現整套監控系統的。

    需要採集、監控的指標項有如下:

    • • QPS

      • • 組合查詢 ,節點 ,服務,region,介面,時間 等

    • • latency

      • • 組合查詢 ,節點 ,服務,region,介面,時間 等

    • • SLA

      • • 指定時間段內請求成功且耗時低於X毫秒的請求個數比例,API介面級別

    • • 消費節點

      • • 當前消費節點的狀態,有多少個服務節點,健康狀態,熔斷狀態等

    配置系統

    我們的一個業務服務,一般,都會有一個對應的配置檔案,而且,我們需要儘可能的做到動態配置,而不是每次修改配置都要重新發布服務。比如說,我們定義好一個 Elasticserach 的賬號密碼,那麼這個最好的方式,肯定是在配置檔案裡面,和程式碼隔離;比如我們定義一個域名字首,而這個字首有可能會改變;比如我們定義一個請求的重試次數,這些,建議都是透過配置檔案來管理。

    每個服務一個配置檔案,微服務化後,大量服務,就會有大量配置檔案,那麼這些配置檔案怎麼統一管理,怎麼讀取,怎麼釋出,就需要一個配置系統,配置系統也需要逐步演進為自動化治理方向,因此就需要一個完善的統一的自動化配置中心。

    日誌系統

    遠端日誌上報

    日誌的重要性不言而喻,每次服務出問題,使用者出問題,我們要查問題,必然要透過日誌,而我們的服務這麼多,部署的機器這麼多,我們不能每次出問題之後,還要去每臺機器上一個個的檢索。為此,這個就必然需要能夠將日誌進行遠端上報,然後統一收集和管理。

    這裡,遠端日誌元件的代表作是 ELK 系統:Elasticsearch, Logstash, Kibana。透過引入 ELK,我們在程式碼裡面,將日誌上報,後續就可以透過 Kibana 去根據情況進行檢索,然後拉取出對應的日誌。

    traceID

    在微服務架構中,一個客戶端請求的接入,往往涉及到後端一系列服務的呼叫,如何將這些請求串聯起來?業界常用的方案是採用全域性流水號【traceID】串聯起來。透過全域性流水號【traceID】,從日誌裡面可以拉出整條呼叫鏈路。

    traceID 需要在所有日誌裡體現,包括訪問日誌、錯誤日誌、警告日誌等任何日誌,唯一標識一個請求。呼叫鏈上每個環節的入口都先判斷是否存在 traceID ,如果沒有 traceID 則生成一個唯一值,存在請求上下文 context 中,輸出日誌的時候就可以把 traceID 列印並輸出。然後呼叫其他服務的時候同時傳遞統一名稱的header 給目標服務,目標服務處理請求是有獲取到 traceID 就設定到自己的請求上線文,以此類推,整個請求鏈條就都使用統一的 traceID。同時 APP 客戶端也可以傳遞一個自己的請求業務標識(可能不一定會唯一),服務端把傳上來的請求標識作為 traceID 的一部分

    分散式鏈路追蹤系統和 OpenTelemetry

    一般公司的後端架構應用構建在不同的服務模組集上,可能是由不同的團隊開發、使用不同的程式語言來實現、部署在多臺伺服器上。對於這種分佈服務系統,傳統查日誌方式排查問題繁瑣,用時較久。因此,需要一個工具能夠梳理這些服務之間的關係,感知上下游服務的形態。比如一次請求的流量從哪個服務而來、最終落到了哪個服務中去?服務之間是RPC呼叫,還是HTTP呼叫?一次分散式請求中的瓶頸節點是哪一個,等等。如果我們需要跟蹤某一個請求在微服務中的完整路徑就要引入我們的分散式鏈路追蹤系統了,目前業內分散式鏈路追蹤系統基本上都是以 Google Dapper (中文版) 為藍本進行設計與開發。

    OpenTracing 制定了一套平臺無關、廠商無關的Trace協議,使得開發人員能夠方便的新增或更換分散式追蹤系統的實現。在2016年11月的時候CNCF技術委員會投票接受OpenTracing作為Hosted專案,這是CNCF的第三個專案,第一個是Kubernetes,第二個是Prometheus,可見CNCF對OpenTracing背後可觀察性的重視。比如大名鼎鼎的Zipkin、Jaeger都遵循OpenTracing協議。

    然後時間走到 2022 年,OpenTracing 都已經快要過時, 現在最優的是 OpenTelemetry。OpenTelemetry 的終態就是實現 Metrics、Tracing、Logging 的融合,作為CNCF可觀察性的終極解決方案,OpenTelemetry 可觀測性領域的標準,OpenTelemetry 的三大資料模型如下:

    • • Tracing:分散式追蹤,觀測請求呼叫鏈路。提供了一個請求從接收到處理完畢整個生命週期的跟蹤路徑,通常請求都是在分散式的系統中處理,所以也叫做分散式鏈路追蹤。

    • • Metrics:指標監控,監控服務質量狀況。提供量化的系統內/外部各個維度的指標,一般包括Counter、Gauge、Histogram等。

    • • Logging:服務日誌,用來分析服務故障。提供系統/程式最精細化的資訊,例如某個關鍵變數、事件、訪問記錄等。

    3-2、微服務體系下的管理平臺

    服務編排管理平臺

    在之前的文章裡面,我們談到,微服務和容器化結合是必然趨勢,在當下,容器化平臺基本都是採用 K8s ,那麼基於 K8s 平臺,我們的服務編排當然就需要圍繞 K8s 來建設了。微服務框架 + K8s 容器平臺 是當今網際網路業務的黃金標準

    如果是採用公有云的話,那麼服務編排的管理平臺,公有云已經幫你提供了。

    如果是私有云的話,那麼我們也需要基於 K8s 來搭建一套我們自己的服務編排的管理平臺,用來幫助開發人員更好的去釋出服務、線上管理服務。這個平臺上的的功能可以包括:

    • • 服務釋出和管理

      • • 灰度釋出

      • • 服務重啟/停止

      • • 支援回滾

    • • 服務登入

      • • 登入到服務所在的容器裡面

    • • 服務擴縮容

      • • 手動擴縮容

      • • 自動擴縮容

    • • 打通外部平臺

      • • 如監控平臺、日誌平臺

      • • 如服務限流和降級平臺

    • • 支援 debug 管理工具

      • • 比如可以動態檢視 go 相關的 runtime 資訊,或者 pprof 等

    DevOps 平臺

    微服務體系下,有大量的服務需要進行運維管理,包括測試、釋出上線、灰度放量、擴縮容等,那我們怎麼合理的去處理好這些運維能力呢,這裡就必須要有一套自動化運維管理體系,而這套東西,在業界,有一個叫做 DevOps 的文化,基於 DevOps 可以方便快捷的實現 CICD。CI 就是持續整合,是一種質量反饋機制,目的是儘快發現程式碼中的質量問題,CD 就是 持續部署,是指能夠自動化、多批次、可控的實現軟體部署,控制故障的影響範圍或方便輕鬆的解決故障問題。透過 DevOps 的建設,可以實現軟體生產流水線和軟體運維自動化。

    DevOps 平臺這裡,更多的整合程式碼管理平臺、流水線構建、單測測試、介面測試判斷等。比如我們開發完一個功能,那麼程式碼首先要合入到 master,然後對程式碼進行一些檢測,隨後打 tag,構造映象,最後釋出。這一系列的流程,如果沒有一個合適的管理平臺,每個步驟都手動去處理的話,那麼就會相當麻煩,而且耗時。

    DevOps 平臺的一些建議和實戰經驗:

    • • 程式碼分支合入 master 的時候,進行檢測校驗

      • • 程式碼靜態檢查

      • • 程式碼規範檢查

      • • 單測覆蓋度檢測

      • • 介面測試檢測

    • • 檢測透過後,可以合入 master 分支,然後自動打 tag,構造線上釋出的映象包

    • • 映象包構造完後,自動和服務編排管理平臺打通,推送映象到服務編排管理平臺,自動觸發釋出流程

      • • 釋出流程需要有灰度的過程

      • • 可隨時中斷和回滾

    自動化測試平臺

    微服務化會帶來服務模組增多的問題,會在一定程度上帶來開發成本的提升,所以需要透過配套的工具鏈來減少服務化後的開發成本。自動化測試平臺的建設,可以有助於我們在開發測試階段就儘可能的減少系統出現的問題,提供一層保障。自動化測試平臺主要的目的是用來進行介面測試和介面撥測,後續也可以進一步去整合 流量錄製和回放、全鏈路壓測等相關功能。

    自動化測試平臺的主要的目的就是確保你上線釋出的服務是正常執行的,而且儘量做到自動發現問題而不是透過人工反饋後才能知道問題。介面撥測這個事情,類似巡檢,就是每個一段時間,自動呼叫你的後端介面,如果出現非預期的錯誤,那麼就認為異常,可以進行告警。

    由於這些都是基礎功能,因此沒有必要每個團隊都各自搞一套,應該是公司內部統一搞一套這樣的系統。

    來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024923/viewspace-2930575/,如需轉載,請註明出處,否則將追究法律責任。

    相關文章