作者 | 趙奕豪(宿何) Sentinel 開源項目負責人來源|阿里巴巴雲原生公眾號
前言
微服務的穩定性一直是開發者非常關注的話題。隨著業務從單體架構向分布式架構演進以及部署方式的變化,服務之間的依賴關係變得越來越複雜,業務系統也面臨著巨大的高可用挑戰。
在生產環境中大家可能遇到過各種不穩定的情況,比如:
大促時瞬間洪峰流量導致系統超出最大負載,load 飆高,系統崩潰導致用戶無法下單。「黑馬」熱點商品擊穿緩存,DB 被打垮,擠佔正常流量。調用端被不穩定第三方服務拖垮,線程池被佔滿,調用堆積,導致整個調用鏈路卡死。這些不穩定的場景可能會導致嚴重後果,但很多時候我們又容易忽視這些與流量/依賴相關的高可用防護。大家可能想問:如何預防這些不穩定因素帶來的影響?如何針對流量進行高可用的防護?如何保障服務「穩如磐石」?這時候我們就要請出阿里雙十一同款的高可用防護中間件 —— Sentinel。在今年剛剛過去的天貓 雙11 大促中,Sentinel 完美地保障了阿里成千上萬服務 雙11 峰值流量的穩定性,同時 Sentinel Go 版本也在近期正式宣布 GA。下面我們來一起了解下 Sentinel Go 的核心場景以及社區在雲原生方面的探索。
Sentinel 介紹
Sentinel 是阿里巴巴開源的,面向分布式服務架構的流量控制組件,主要以流量為切入點,從限流、流量整形、熔斷降級、系統自適應保護等多個維度來幫助開發者保障微服務的穩定性。Sentinel 承接了阿里巴巴近 10 年的 雙11 大促流量的核心場景,例如秒殺、冷啟動、消息削峰填谷、集群流量控制、實時熔斷下遊不可用服務等,是保障微服務高可用的利器,原生支持 Java/Go/C++ 等多種語言,並且提供 Istio/Envoy 全局流控支持來為 Service Mesh 提供高可用防護的能力。
今年年初,Sentinel 社區宣布了 Sentinel Go 版本的發布,為 Go 語言的微服務和基礎組件提供高可用防護和容錯能力的原生支持,標誌著 Sentinel 朝著多元化與雲原生邁出了新的一步。在這半年的時間內,社區推出了近 10 個版本,逐步對齊了核心高可用防護和容錯能力,同時也在不斷擴充開源生態,與 dubbo-go、螞蟻 MOSN 等開源社區進行共建。
就在近期,Sentinel Go 1.0 GA 版本正式發布,標誌著 Go 版本正式進入生產可用階段。Sentinel Go 1.0 版本對齊了 Java 版本核心的高可用防護和容錯能力,包括限流、流量整形、並發控制、熔斷降級、系統自適應保護、熱點防護等特性。同時 Go 版本已覆蓋主流開源生態,提供了 Gin、gRPC、go-micro、dubbo-go 等常用微服務框架的適配,並提供了 etcd、Nacos、Consul 等動態數據源擴展支持。Sentinel Go 也在朝著雲原生的方向不斷演進,1.0 版本中也進行了一些雲原生方面的探索,包括 Kubernetes CRD data-source, Kubernetes HPA 等。對於 Sentinel Go 版本而言,我們期望的流控場景並不局限於微服務應用本身。雲原生基礎組件中 Go 語言生態佔比較高,而這些雲原生組件很多時候又缺乏細粒度、自適應的保護與容錯機制,這時候就可以結合組件的一些擴展機制,利用 Sentinel Go 來保護自身的穩定性。Sentinel 底層通過高性能的滑動窗口進行秒級調用指標統計,結合 token bucket, leaky bucket 和自適應流控算法來透出核心的高可用防護能力。
那麼我們如何利用 Sentinel Go 來保證我們微服務的穩定性?下面我們來看幾個典型的應用場景。
高可用防護的核心場景
1. 流量控制與調配
流量是非常隨機性的、不可預測的。前一秒可能還風平浪靜,後一秒可能就出現流量洪峰了(例如 雙11 零點的場景)。然而我們系統的容量總是有限的,如果突然而來的流量超過了系統的承受能力,就可能會導致請求處理不過來,堆積的請求處理緩慢,CPU/Load 飆高,最後導致系統崩潰。因此,我們需要針對這種突發的流量來進行限制,在儘可能處理請求的同時來保障服務不被打垮,這就是流量控制。流量控制的場景是非常通用的,像脈衝流量類的場景都是適用的。
通常在 Web 入口或服務提供方(Service Provider)的場景下,我們需要保護服務提供方自身不被流量洪峰打垮。這時候通常根據服務提供方的服務能力進行流量控制,或針對特定的服務調用方進行限制。我們可以結合前期壓測評估核心接口的承受能力,配置 QPS 模式的流控規則,當每秒的請求量超過設定的閾值時,會自動拒絕多餘的請求。
下面是最簡單的一個 Sentinel 限流規則的配置示例:
_, err = flow.LoadRules([]*flow.Rule{ { Resource:"some-service", // 埋點資源名 Count:10, // 閾值為 10,默認為秒級維度統計,即該請求單機每秒不超過 10 次 ControlBehavior: flow.Reject, // 控制效果為直接拒絕,不控制請求之間的時間間隔,不排隊 },})
2. Warm-Up 預熱流控
當系統長期處於低水位的情況下,流量突然增加時,直接把系統拉升到高水位可能瞬間把系統壓垮。比如剛啟動的服務,資料庫連接池可能還未初始化,緩存也處於空的狀態,這時候激增的流量非常容易導致服務崩潰。如果採用傳統的限流模式,不加以平滑/削峰限制,其實也是有被打掛的風險的(比如一瞬間並發很高)。針對這種場景,我們就可以利用 Sentinel 的 Warm-Up 流控模式,控制通過的流量緩慢增加,在一定時間內逐漸增加到閾值上限,而不是在一瞬間全部放行,同時結合請求間隔控制+排隊的控制效果 來防止大量請求都在同一時刻被處理。這樣可以給冷系統一個預熱的時間,避免冷系統被壓垮。
3. 並發控制與熔斷降級
一個服務常常會調用別的模塊,可能是另外的一個遠程服務、資料庫,或者第三方 API 等。例如,支付的時候,可能需要遠程調用銀聯提供的 API;查詢某個商品的價格,可能需要進行資料庫查詢。然而,這個被依賴服務的穩定性是不能保證的。如果依賴的服務出現了不穩定的情況,請求的響應時間變長,那麼調用服務的方法的響應時間也會變長,線程會產生堆積,最終可能耗盡業務自身的線程池,服務本身也變得不可用。
現代微服務架構都是分布式的,由非常多的服務組成。不同服務之間相互調用,組成複雜的調用鏈路。以上的問題在鏈路調用中會產生放大的效果。複雜鏈路上的某一環不穩定,就可能會層層級聯,最終導致整個鏈路都不可用。Sentinel Go 提供以下的能力避免慢調用等不穩定因素造成不可用:
並發控制(isolation 模塊):作為一種輕量級隔離的手段,控制某些調用的並發數(即正在進行的數目),防止過多的慢調用擠佔正常的調用。熔斷降級(circuitbreaker 模塊):對不穩定的弱依賴調用進行自動熔斷降級,暫時切斷不穩定調用,避免局部不穩定因素導致整體的雪崩。Sentinel Go 熔斷降級特性基於熔斷器模式的思想,在服務出現不穩定因素(如響應時間變長,錯誤率上升)的時候暫時切斷服務的調用,等待一段時間再進行嘗試。一方面防止給不穩定服務「雪上加霜」,另一方面保護服務的調用方不被拖垮。Sentinel 支持兩種熔斷策略:基於響應時間(慢調用比例)和基於錯誤(錯誤比例/錯誤數),可以有效地針對各種不穩定的場景進行防護。
注意熔斷器模式一般適用於弱依賴調用,即降級後不影響業務主流程,開發者需要設計好降級後的 fallback 邏輯和返回值。另外需要注意的是,即使服務調用方引入了熔斷降級機制,我們還是需要在 HTTP 或 RPC 客戶端配置請求超時時間,來做一個兜底的防護。
4. 熱點防護
流量是隨機的,不可預測的。為了防止被大流量打垮,我們通常會對核心接口配置限流規則,但有的場景下配置普通的流控規則是不夠的。我們來看這樣一種場景——大促峰值的時候,總是會有不少「熱點」商品,這些熱點商品的瞬時訪問量非常高。一般情況下,我們可以事先預測一波熱點商品,並對這些商品信息進行緩存「預熱」,以便在出現大量訪問時可以快速返回而不會都打到 DB 上。但每次大促都會湧現出一些「黑馬」商品,這些「黑馬」商品是我們無法事先預測的,沒有被預熱。當這些「黑馬」商品訪問量激增時,大量的請求會擊穿緩存,直接打到 DB 層,導致 DB 訪問緩慢,擠佔正常商品請求的資源池,最後可能會導致系統掛掉。這時候,利用 Sentinel 的熱點參數流量控制,自動識別熱點參數並控制每個熱點值的訪問 QPS 或並發量,可以有效地防止過「熱」的參數訪問擠佔正常的調用資源。
再比如有的場景下我們希望限制每個用戶調用某個 API 的頻率,將 API 名稱+userId 作為埋點資源名顯然是不合適的。這時候我們可以在給 API 埋點的時候通過
WithArgs(xxx)
將 userId 作為參數傳入到 API 埋點中,然後配置熱點規則即可針對每個用戶分別限制調用頻率;同時,Sentinel 也支持針對某些具體值單獨配置限流值,進行精細化流控。像其他規則一樣,熱點流控規則同樣支持通過動態數據源進行動態配置。Sentinel Go 提供的 RPC 框架整合模塊(如 Dubbo、gRPC)均會自動將 RPC 調用的參數列表附帶在埋點中,用戶可以直接針對相應的參數位置配置熱點流控規則。注意如果需要配置具體值限流,受類型系統限制,目前僅支持基本類型和 string 類型。
Sentinel Go 的熱點流量控制基於緩存淘汰機制+令牌桶機制實現。Sentinel 通過淘汰機制(如 LRU、LFU、ARC 策略等)來識別熱點參數,通過令牌桶機制來控制每個熱點參數的訪問量。目前的 Sentinel Go 版本採用 LRU 策略統計熱點參數,社區也已有貢獻者提交了優化淘汰機制的 PR,在後續的版本中社區會引入更多的緩存淘汰機制來適配不同的場景。
5. 系統自適應保護
有了以上的流量防護場景,是不是就萬事無憂了呢?其實不是的,很多時候我們無法事先就準確評估某個接口的準確容量,甚至無法預知核心接口的流量特徵(如是否有脈衝情況),這時候靠事先配置的規則可能無法有效地保護當前服務節點;一些情況下我們可能突然發現機器的 Load 和 CPU usage 等開始飈高,但卻沒有辦法很快的確認到是什麼原因造成的,也來不及處理異常。這個時候我們其實需要做的是快速止損,先通過一些自動化的兜底防護手段,將瀕臨崩潰的微服務「拉」回來。針對這些情況,Sentinel Go 提供了一種系統自適應保護規則,結合系統指標和服務容量,自適應動態調整流量。
Sentinel 系統自適應保護策略借鑑了 TCP BBR 算法的思想,結合系統的 Load、CPU 使用率以及服務的入口 QPS、響應時間和並發量等幾個維度的監控指標,通過自適應的流控策略,讓系統的入口流量和系統的負載達到一個平衡,讓系統儘可能跑在最大吞吐量的同時保證系統整體的穩定性。系統規則可以作為整個服務的一個兜底防護策略,保障服務不掛,對 CPU 密集型的場景會有比較好的效果。同時,社區也在結合自動化控制理論和強化學習等手段,持續完善自適應流控的效果和適用場景。在未來的版本中,社區會也推出更多試驗性的自適應流控策略,來滿足更多的可用性場景。
雲原生探索
雲原生是 Sentinel Go 版本演進最為重要的一環。在 GA 的過程中,Sentinel Go 社區也在 Kubernetes 和 Service Mesh 等場景下進行了一些探索。
1. Kubernetes CRD data-source
在生產環境中我們一般都需要通過配置中心來動態管理各種規則配置。在 Kubernetes 集群中,我們可以天然利用 Kubernetes CRD 的方式來管理應用的 Sentinel 規則。在 Go 1.0.0 版本中社區提供了基本的 Sentinel 規則 CRD 抽象以及相應的 數據源實現。用戶只需要先導入 Sentinel 規則 CRD 定義文件,接入 Sentinel 時註冊對應的 data-source,然後按照 CRD 定義的格式編寫 YAML 配置並 kubectl apply 到對應的 namespace 下即可實現動態配置規則。以下是一個流控規則的示例:
apiVersion:datasource.sentinel.io/v1alpha1kind:FlowRulesmetadata:name:foo-sentinel-flow-rulesspec:rules:-resource:simple-resourcethreshold:500-resource:something-to-smooththreshold:100controlBehavior:ThrottlingmaxQueueingTimeMs:500-resource:something-to-warmupthreshold:200tokenCalculateStrategy:WarmUpcontrolBehavior:RejectwarmUpPeriodSec:30warmUpColdFactor:3
Kubernetes CRD data-source 模塊地址:https://github.com/sentinel-group/sentinel-go-datasource-k8s-crd
後續社區會進一步完善 Rule CRD 定義並與其它社區一起探討高可用防護相關的標準抽象。
2. Service Mesh
Service Mesh 是微服務向雲原生演進的趨勢之一。在 Service Mesh 架構下,一些服務治理和策略控制的能力都逐漸下沉到了 data plane 層。去年 Sentinel 社區在 Java 1.7.0 版本裡面做了一些嘗試,提供了 Envoy Global Rate Limiting gRPC Service 的實現 —— Sentinel RLS token server,藉助 Sentinel 集群限流 token server 來為 Envoy 服務網格提供集群流量控制的能力。今年隨著 Sentinel Go 版本的誕生,社區與更多的 Service Mesh 產品開展合作、整合。我們與螞蟻的 MOSN 社區進行共建,在 MOSN Mesh 中原生支持了 Sentinel Go 的流控降級能力,同時也已在螞蟻內部落地。社區也在探索更為通用的方案,如利用 Istio 的 Envoy WASM 擴展機制實現 Sentinel 插件,讓 Istio/Envoy 服務網格可以藉助 Sentinel 原生的流控降級與自適應保護的能力來保障整個集群服務的穩定性。
3. Kubernetes HPA based on Sentinel metrics
保障服務穩定性的方法多種多樣,除了各種規則對流量進行「控制」之外,「彈性」也是一種思路。對於部署在 Kubernetes 中的應用來說,可以利用 Kubernetes HPA 能力進行對服務進行水平擴縮容。HPA 默認支持多種系統指標,並且支持自定義指標統計。目前我們已經在阿里雲 Kubernetes 容器服務上結合 AHAS Sentinel 支持基於服務的平均 QPS、響應時間等作為條件進行彈性伸縮。社區也正在這一塊做一些嘗試,將一些 Sentinel 的服務級別的指標統計(通過量,拒絕量,響應時間等)通過 Prometheus 或 OpenTelemetry 等標準方式透出,並適配到 Kubernetes HPA 中。
當然基於 Sentinel 的彈性方案不是萬靈藥,它只適用於一些特定的場景,比如適用於啟動速度快的無狀態服務(Serverless 場景)。對於啟動較慢的服務,或非本服務容量問題的場景(如依賴的 DB 容量不夠),彈性的方案不能很好地解決穩定性的問題,甚至可能會加劇服務的惡化。
Let's start hacking!
了解了以上的高可用防護的場景,以及 Sentinel 在雲原生方向的一些探索,相信大家對微服務容錯與穩定性的手段有了新的體會。歡迎大家動手玩一下 demo,將微服務接入 Sentinel 來享受高可用防護和容錯的能力,讓服務「穩如磐石」。同時 Sentinel Go 1.0 GA 版本的發布離不開社區的貢獻,感謝所有參與貢獻的小夥伴們。
本次 GA 我們也新加入了兩位給力的 committer —— @sanxun0325 和 @luckyxiaoqiang,兩位在 1.0 版本的演進帶來了 Warm Up 流控、Nacos 動態數據源以及一系列功能改進和性能優化,非常積極地幫助社區答疑解惑以及 review 代碼。恭喜兩位!社區在未來版本中也會朝著雲原生和自適應智能化的方向不斷探索和演進,也歡迎更多的同學加入貢獻小組,一起參與 Sentinel 未來的演進,創造無限可能。我們鼓勵任何形式的貢獻,包括但不限於:
bug fixnew features/improvementsdashboarddocument/websitetest cases本文為阿里雲原創內容,未經允許不得轉載。