1 微服務優勢與挑戰
1.1 微服務的優勢
1.1.1 單一職責
1.1.2 輕量級通訊
通過REST API模式或者RPC框架,事件流和訊息代理的組合相互通訊,實現服務間互相協作的輕量級通訊機制。
1.1.3 獨立性
在微服務架構中,每個服務都是獨立的業務單元,與其他服務高度解耦,只需要改變當前服務本身,就可以完成獨立的開發、測試、部署、運維。
1.1.4 程式隔離
1.1.5 混合技術棧和混合部署方式
團隊可以為不同的服務元件使用不同的技術棧和不同的部署方式(公有云、私有云、混合雲)。
1.1.6 簡化治理
元件可以彼此獨立地進行縮放,從而減少了因必須縮放整個應用程式而產生的浪費和成本,獨立的釋出、服務治理。
1.1.7 安全可靠,可維護。
從架構上對運維提供友好的支撐,在安全、可維護的基礎上規範化釋出流程,支援資料儲存容災、業務模組隔離、訪問許可權控制、編碼安全檢測等。
1.2 面臨的挑戰
1.2.1 分散式固有複雜性
- 效能: 分散式系統是跨程式、跨網路的呼叫,受網路延遲和頻寬的影響。
- 可靠性: 由於高度依賴於網路狀況,任何一次的遠端呼叫都有可能失敗,隨著服務的增多還會出現更多的潛在故障點。因此,如何提高系統的可靠性、降低因網路引起的故障率,是系統構建的一大挑戰。
- 分散式通訊: 分散式通訊大大增加了功能實現的複雜度,並且伴隨著定位難、除錯難等問題。
- 資料一致性: 需要保證分散式系統的資料強一致性,即在 C(一致性)A(可用性)P(分割槽容錯性) 三者之間做出權衡。這塊可以參考我的這篇《分散式事務》。
1.2.2 服務的依賴管理和測試
在單體應用中,通常使用整合測試來驗證依賴是否正常。而在微服務架構中,服務數量眾多,每個服務都是獨立的業務單元,服務主要通過介面進行互動,如何保證它的正常,是測試面臨的主要挑戰。
所以單元測試和單個服務鏈路的可用性非常重要。
1.2.3 有效的配置版本管理
在單體系統中,配置可以寫在yaml檔案,分散式系統中需要統一進行配置管理,同一個服務在不同的場景下對配置的值要求還可能不一樣,所以需要引入配置的版本管理、環境管理。
1.2.4 自動化的部署流程
1.2.5 對於DevOps更高的要求
1.2.6 運維成本
運維主要包括配置、部署、監控與告警和日誌收集四大方面。微服務架構中,每個服務都需要獨立地配置、部署、監控和收集日誌,成本呈指數級增長。
服務化粒度越細,運維成本越高。
怎樣去解決這些問題,是微服務架構必須面臨的挑戰。
2 微服務全景架構
3 微服務核心元件
微服務架構核心元件包括:
元件名 |
服務註冊與發現 |
API 閘道器服務 |
分散式配置中心 |
服務通訊 |
服務治理 |
服務監控 |
分散式服務追蹤 |
3.1 服務註冊與發現
3.1.1 原理圖
服務註冊與發現三要素:
- Provider:服務的提供方
- Consumer:呼叫遠端服務的服務消費方
- Registry:服務註冊和發現的註冊中心
3.1.2 註冊中心的原理、流程
1、 Provider(服務提供者)繫結指定埠並啟動服務
2、提供者連線註冊中心,併發本機 IP、埠、應用資訊和服務資訊傳送至註冊中心儲存
3、Consumer(消費者),連線註冊中心 ,併傳送應用資訊、所求服務資訊至註冊中心
4、註冊中心根據消費者所求服務資訊匹配對應的提供者列表傳送至Consumer 應用快取。
5、Consumer 在發起遠端呼叫時基於快取的消費者列表擇其一發起呼叫。
6、Provider 狀態變更會實時通知註冊中心、在由註冊中心實時推送至Consumer設計的原因:
Consumer 與 Provider 解偶,雙方都可以橫向增減節點數。註冊中心對本身可做對等叢集,可動態增減節點,並且任意一臺宕掉後,將自動切換到另一臺
7、去中心化,雙方不直接依賴註冊中心,即使註冊中心全部當機短時間內也不會影響服務的呼叫(Consumer應用快取中保留提供者 Provider 列表)
8、服務提供者無狀態,任意一臺宕掉後,不影響使用
註冊中心包含如下功能:註冊中心、服務註冊和反註冊、心跳監測與彙報、服務訂閱、服務變更查詢、叢集部署、服務健康狀態檢測、服務狀態變更通知 等
我們有很多種註冊中心的技術,Zookeeper、Etcd、Consul、Eureka 4種比較常用,如下
Zookeeper | Etcd | Consul | Eureka | |
---|---|---|---|---|
CAP模型 | CP | CP | CP | AP |
資料一致性演算法 | ZAB | Raft | Raft | ❌ |
多資料中心 | ❌ | ❌ | ✅ | ❌ |
多語言支援 | 客戶端 | Http/gRPC | Http/DNS | Http |
Watch | TCP | Long Polling | Long Polling | Long Polling |
KV儲存 | ✅ | ✅ | ✅ | ❌ |
服務健康檢查 | 心跳 | 心跳 | 服務狀態, 記憶體,硬碟等 |
自定義 |
自身監控 | ❌ | metrics | metrics | metrics |
SpringCloud 支援 | ✅ | ✅ | ✅ | ✅ |
自身開發語言 | Java | Go | Go | Java |
分散式系統中CAP模型3者不可兼得。由於網路的原因,分散式系統中P是必備的,意味著只能選擇 AP 或者 CP。CP 代表資料一致性是第一位的,AP 代表可用性是第一位的。
Zookeeper、Etcd、Consul 是 CP 型註冊中心,犧牲可用性來保證資料強一致性
Eureka 是 AP 型註冊中心,犧牲一致性來保證可用性
3.2 API 閘道器服務
上面是Api閘道器服務的基本架構:使用者的請求經過統一的Api閘道器來訪問微服務裡具體的服務顆粒,並且可能產生串聯的鏈路服務呼叫。
有很多耳熟能詳的API閘道器技術,比如 Zuul、Kong、Tyk等,提供了服務路由在內的很多通用功能,後面會有專門的章節來說這個。
Tyk:Tyk是一個開放原始碼的API閘道器,它是快速、可擴充套件和現代的。Tyk提供了一個API管理平臺,其中包括API閘道器、API分析、開發人員門戶和API管理皮膚。Try 是一個基於Go實現的閘道器服務。
Kong:Kong是一個可擴充套件的開放原始碼API Layer(也稱為API閘道器或API中介軟體)。Kong 在任何RESTful API的前面執行,通過外掛擴充套件,它提供了超越核心平臺的額外功能和服務。
Netflix zuul:Zuul是一種提供動態路由、監視、彈性、安全性等功能的邊緣服務。Zuul是Netflix出品的一個基於JVM路由和服務端的負載均衡器。
除了路由之外,Api閘道器服務還包含:認證和授權,重試、熔斷、降級,負載均衡,日誌、監控、鏈路追蹤,灰度釋出,ABTesting 等功能。
3.3 配置中心
上面這個是攜程的開源配置中心Apollo系統的架構設計,我們從下往上進行分析:
1、Config Service提供配置的讀取、推送等功能,服務物件是Apollo客戶端
2、Admin Service提供配置的修改、釋出等功能,服務物件是Apollo Portal(管理介面)
3、Config Service和Admin Service都是多例項、無狀態部署,所以需要將自己註冊到Eureka中並保持心跳,支援註冊、更新、刪除能力
4、在Eureka之上我們架了一層Meta Server用於封裝Eureka的服務發現介面
5、Client通過域名訪問Meta Server獲取Config Service服務列表(IP+Port),而後直接通過IP+Port訪問服務,同時在Client側會做load balance、錯誤重試
6、Portal通過域名訪問Meta Server獲取Admin Service服務列表(IP+Port),而後直接通過IP+Port訪問服務,同時在Portal側會做load balance、錯誤重試
7、為了簡化部署,我們實際上會把Config Service、Eureka和Meta Server三個邏輯角色部署在同一個JVM程式中
上面的架構體現瞭如下特點:
•高可用:配置服務為多例項部署,訪問層保證 load balance、錯誤重試
•弱依賴:使用了Eureka來做配置中心的服務註冊,如果出現問題或者網路出現問題的時候,服務應該可以依賴於它本身所快取的配置來提供正常的服務
3.4 服務通訊
分散式系統一般是由多個微服務顆粒組成的,微服務與微服務之前存在互相呼叫,甚至多個鏈路訪問的情況。所以他們之間是需要通訊的,通訊方式繼承於SOA,包含同步與非同步兩種模式。
3.4.1 同步訪問方式
1、RPC 訪問模式
2、REST 訪問模式
這個應該大家最常用,可以通過一套統一風格的介面模式,為Web,iOS和Android等提供介面服務。
3.4.2 非同步訪問方式
訊息中介軟體:RabbitMQ、Kafka、RocketMQ之類,對於實時性要求不那麼嚴格的服務請求和計算。
3.5 服務治理
常見的服務治理手段有如下幾種:
3.5.1 節點管理
服務呼叫失敗時可能是服務提供者自身出現,也可能是網路發生故障,我們一般有兩種處理手段。
1. 註冊中心主動摘除機制
這種機制要求服務提供者定時向註冊中心彙報心跳,如果超時,就認為服務提供者出現問題,並將節點從服務列表中摘除。
2. 服務消費者摘除機制
當服務提供者網路出現異常,服務消費者呼叫就會失敗,如果持續錯誤就可以將它從服務提供者節點列表中移除。
3.5.2 負載均衡
服務消費者在從服務列表中選取可用節點時,如果能讓效能較好的服務機多承擔一些流量的話,就能充分利用機器的效能。這就需要對負載均衡演算法做一些調整。
常用的負載均衡演算法主要包括以下幾種:
1. Radom 隨機演算法
從可用的服務節點中隨機選取一個節點。一般情況下,隨機演算法是均勻的,也就是說後端服務節點無論配置好壞,最終得到的呼叫量都差不多。
2. Round Robin 輪詢演算法(加權重)
就是按照固定的權重,對可用服務節點進行輪詢。如果所有服務節點的權重都是相同的,則每個節點的呼叫量也是差不多的。但可以給效能較好的節點的權重調大些,充分發揮其效能優勢,提高整體呼叫的平均效能。
3. Least Conn 最少活躍呼叫演算法
這種演算法是在服務消費者這一端的記憶體裡動態維護著同每一個服務節點之間的連線數,選擇連線數最小的節點發起呼叫,也就是選擇了呼叫量最小的服務節點,效能理論上也是最優的。
4. 一致性 Hash 演算法
指相同引數的請求總是發到同一服務節點。當某一個服務節點出現故障時,原本發往該節點的請求,基於虛擬節點機制,平攤到其他節點上,不會引起劇烈變動。
3.5.3 服務路由
所謂的路由規則,就是通過一定的規則如條件表示式或者正規表示式來限定服務節點的選擇範圍。
制定路由規則主要有兩個原因。
1. 業務存在灰度釋出、多版本ABTesting的需求
功能逐步開放釋出或者灰度測試的場景。
2. 多機房就近訪問的需求
一般可以通過 IP 段規則來控制訪問,在選擇服務節點時,優先選擇同一 IP 段的節點。這個也是算力靠近的優先原則。
3.5.4 服務容錯
在分散式系統中,分割槽容錯性是很重要的一個話題,要知道,服務間的呼叫呼叫並不總是成功,服務提供者程式bug、異常退出 或者 消費者與提供者之間的網路故障。而服務呼叫失敗之後,我們需要一些方法來保證呼叫的正常。
常用的方式有以下幾種:
FailOver 失敗自動切換。就是服務消費者發現呼叫失敗或者超時後,自動從可用的服務節點列表中選擇下一個節點重新發起呼叫,也可以設定重試的次數。
FailBack 失敗通知。就是服務消費者呼叫失敗或者超時後,不再重試,而是根據失敗的詳細資訊,來決定後續的執行策略。
FailCache 失敗快取。就是服務消費者呼叫失敗或者超時後,不立即發起重試,而是隔一段時間後再次嘗試發起呼叫。
FailFast 快速失敗。就是服務消費者呼叫一次失敗後,不再重試。
服務治理的手段是從不同角度來確保服務呼叫的成功率。節點管理是從服務節點健康狀態角度來考慮,負載均衡和服務路由是從服務節點訪問優先順序角度來考慮,而服務容錯是從呼叫的健康狀態角度來考慮。
3.6 服務監控
常見的開發監控報警技術有 ELK、InfluxData的TICK、Promethues 等。
在分散式系統中,微服務一般都具有複雜的鏈路呼叫,對於鏈路之間的狀態、服務可用性、呼叫情況的監控,是需要一套完整的服務監控系統去保障的。
如我們上面的那個圖所示, 服務系統主要由哪幾部分構成:
1、資料採集部分,包含效能指標資訊、日誌資訊(一般是服務埋點日誌或者sidecar的inbound、outbound資訊)、端到端的Trace資訊。
2、採集上來的監控資料通過傳輸系統,或者使用訊息中介軟體來非同步傳輸,或者呼叫服務端介面推送監控資料。並把這些資料持久化到我們的資料服務層中。
3、制定一套規則,對於採集到的資料進行清理、計算、分級等,處理好的資料,通過提前設定好的報警策略,來判斷它是否觸發了這些報警。
4、梳理完的資料可以進行查詢展示(有一個日誌查詢介面)、分級報警、分析趨勢報表推送等。
3.7 服務追蹤
服務追蹤的原理主要包括下面兩個關鍵點。
1、為了實現請求跟蹤,當請求傳送到分散式系統的入口端點時,只需要服務跟蹤框架為該請求建立一個唯一的跟蹤標識,同時在分散式系統內部流轉的時候,框架始終保持傳遞該唯一標識,直到返回給請求方為止,這個唯一標識就是前文中提到的 Trace ID。
通過 Trace ID 的記錄,我們就能將所有請求過程的日誌關聯起來。
2、為了統計各處理單元的時間延遲,當請求到達各個服務元件時,或是處理邏輯到達某個狀態時,也通過一個唯一標識來標記它的開始、具體過程以及結束,該標識就是前文中提到的 Span ID。對於每個 Span 來說,它必須有開始和結束兩個節點,
通過記錄開始 Span 和結束 Span 的時間戳,就能統計出該 Span 的時間延遲,除了時間戳記錄之外,它還可以包含一些其他後設資料,比如事件名稱、請求資訊等。
上圖顯示了Trace ID 和 Spand ID 在鏈路中的傳輸過程,它把服務呼叫的一個時序結構給展現出來了。
常見的服務鏈路追蹤的技術有Zipkin、Pinpoint、SkyWalking 等。後面講到Service Mesh的時候會詳細說下Zipkin的x-b3 header頭傳遞,以及流量染色的使用,非常給力。
4 總結
微服務架構提倡的單一應用程式劃分成一組鬆散耦合的細粒度小型服務,輔助輕量級的協議,互相協調、互相配合,實現高效的應用價值,符合我們應用服務開發的發展趨勢。