阿里巴巴開源限流降級神器Sentinel大規模生產級應用實踐

咖啡拿鐵發表於2022-12-05

阿里巴巴開源限流降級神器Sentinel大規模生產級應用實踐

作者:丁浪,目前在創業公司擔任高階技術架構師。曾就職於阿里巴巴大文娛和螞蟻金服。具有豐富的穩定性保障,全鏈路效能最佳化的經驗。架構師社群特邀嘉賓!

前言

網際網路上關於限流演算法、Sentinel功能介紹、基本結構、原理分析的文章可謂汗牛充棟,我並不打算重複製造內容。我將為大家分享在實際工作和生產環境中使用、踩坑的經驗。

如果你正在做限流、熔斷的技術選型,那麼本文將會為你提供客觀、有價值的參考;

如果你將來要在生產環境中使用Sentinel,那麼本文將會幫助你後續少走彎路;

如果你正在準備求職面試,或許可以幫你的技能樹和經驗上增加亮點,避免你的面試評價表上被人寫上“紙上談兵”;

開源版Sentinel和阿里內部的是一樣的嗎?

我們可以在大規模生產級應用嗎?

這裡我先直接告訴大家答案:開源的和內部版本是一樣的,最核心的程式碼和能力都開源出來了。可以生產級應用,但並非 “開箱即用”,需要你做一些二次開發和調整,接下來我會對這些問題仔細展開。當然,我更推薦你直接使用阿里雲上的AHASSentinel 控制檯和ASM配置中心,那是最佳實踐的輸出,你可以節省很多時間、人力、運維成本等。

總體執行架構

阿里巴巴開源限流降級神器Sentinel大規模生產級應用實踐

大規模生產級應用面臨的問題

看完Sentinel開源版原始的執行架構,很明顯其中存在一些問題:

1.    限流、降級等規則儲存在應用節點的記憶體中,應用釋出重啟後就會失效,這在生產環境中顯然是無法接受的;

2.    規則下發預設是按照機器節點維度的而非按應用維度,而正常公司的應用系統都是叢集部署的,而且這樣也無法支援叢集限流;

3.    metrics資訊由Dashboard拉取上來後儲存到記憶體中,僅僅保留5分鐘,錯過後可能無法還原 “案發現場”,而且無法看到流量趨勢;

4.    如果接入限流的應用有500+個,每個應用平均部署4個節點,那麼總共2000個節點,那麼Dashboard肯定會成為瓶頸,單機的執行緒池根本處理不過來;

如何最佳化並解決這些問題

接下來,我們就先一一的介紹如何解決上述這些明顯的問題。

首先,限流規則、降級規則等都應該按照應用維度去下發,而不是按照APP單節點的維度。因為Sentinel支援叢集限流,所以開源版本的SentinelDashbord 在針對限流規則這塊已經做了擴充套件,但對於熔斷、系統保護等還未擴充套件支援按應用維度下發,感興趣的讀者可以參考FlowControllerV2的實現去實現。

其次,規則不應該儲存記憶體中,應該持久化到動態配置中心,而應用直接從配置中心訂閱規則即可。這樣,Dashboard和應用就透過配置中心實現解耦了,這是典型的生產者消費者模式,基本執行架構如下:

阿里巴巴開源限流降級神器Sentinel大規模生產級應用實踐 

以nacos配置中心為例,Sentinel官方和社群提供了限流規則儲存和訂閱的Demo,接下來熔斷降級、系統保護、閘道器限流…等規則你其實都可以“照貓畫虎”的去擴充套件。基本模式就是:Dashboard將xxRuleEntityVO模型序列化後儲存到nacos,應用從nacos訂閱後反序列成xxRule領域模型。

這裡我特別提醒一下各位前方有巨坑,“熱點引數限流規則”和“黑名單限制規則”這塊請勿直接照搬,因為Dashboard 中定義的ParamFlowRuleEntity、AuthorityRuleEntity

這2個VO模型和領域模型ParamFlowRule、AuthorityRule中的欄位定義不匹配,會導致序列化/反序列化失敗的問題,繼而導致應用無法訂閱和使用熱點引數限流規則和黑名單限制規則,這塊我會提交PR!!!

第3點,Dashboard中有個排程執行緒池,會輪詢方式請求(預設是每隔1秒鐘發起)各應用的機器節點查詢metrics日誌資訊,聚合後並在介面上做監控展示(改造後還需完成持久化動作)。這是典型的pull模式,是目前監控度量領域比較通用的架構。因為是儲存記憶體中所以預設僅保留5分鐘,這也是有問題的。推薦有如下幾種解法:

1.    Dashboard在拉取到metrics資訊後,直接儲存到時序資料庫中,Dashboard自身也從時序資料庫中取資料展示。metrics資料存多久,這個你自己根據業務來決定。以開源的Influxdb為例其自帶持久策略功能(資料過期自動清理)。並且,你還可以藉助Grafana等開源Dashboard做查詢、聚合,展示各種漂亮的大盤、圖形、排行榜等;

2.    你可以將pull的模式改為push模式,在記錄metrics日誌時改為直接寫時序資料庫,當你,基於效能的考慮你也可以改為寫MQ做緩衝。除了耗時,最關鍵的是不能因為這個記錄metrics的動作影響到主業務流程的推進;

3.    繼續列印metrics日誌,啟用SentinelDashboard拉取metrics資料,改用直接在應用機器節點上透過採集器對metrics日誌做採集、處理、上報,可以藉助ELK等工具;

4.    你可以嘗試自己開發PrometheusExporter,自己將metrics資訊以Target形式暴露出去,由Prometheus服務端定時去拉取,同時你也可以使用Prometheus提供的各種豐富的查詢、聚合的語法和能力,透過Grafana等做展示;

下圖是典型的時序資料的例子,天生就是為metrics指標資料而設計的,該領域比較知名的開源軟體有OpenTSDB、Influxdb等。

阿里巴巴開源限流降級神器Sentinel大規模生產級應用實踐


Grafana限流大盤展示效果圖

以上方式各有優劣,如果你想改動最小,並且你們應用接入和部署規模並不是特別大(500節點以內),那麼請選擇第1種方式。

阿里巴巴開源限流降級神器Sentinel大規模生產級應用實踐

第4點,關於接入應用和節點較多導致Dashboard在拉取、聚合時的效能瓶頸。在解決問題3的時候,如果你選擇了2,3,4這幾種方法,那麼Sentinel自帶的Dashboard將僅僅作為規則下發的工具(甚至規則下發都可以直接透過nacos配置中心的控制檯完成),自然就不會有瓶頸的問題了。如果你依舊想借助Sentinel自帶的Dashboard來完成metrics資料的拉取和持久化等任務,那麼我提供給你有兩個解法:

1.    按領域隔離,不同業務域的應用連線到各自的SentinelDashboard上,這樣自然就分攤壓力減少瓶頸出現的可能性了。優點就是幾乎無需做改造,缺點就是顯得不統一;

2.    你可以嘗試改造Sentinel自帶的Dashboard,讓其具備無狀態性。前面我們提過,應用端啟動後會定時上報心跳資訊,Dashboard這邊預設會在記憶體中維護一份 “節點資訊列表”的資料,這個是典型的狀態性資料,應該考慮放到集中儲存中,例如:redis中。然後你需要改造“拉取metrics資訊”的執行緒池,改為分片任務方式去執行,這樣達到分攤負載的作用,例如:改為使用elasticjob排程。當然,時序資料庫的寫入也是有可能成為瓶頸的;

3.    你可以犧牲一點監控指標的時效性,將Sentinel Dashboard中fetchScheduleService排程執行緒池的間隔時間引數調大一點,這樣可以緩解下游工作執行緒池的處理壓力;

就我個人而言其實更推薦第1,3這兩種方式,這都是改動比較小的權宜之計。

當然,按領域劃分其實也是有其他好處的。你試想如果接入了500+系統,以目前開源版的Dashboard為例,左側應用列表得拉多長?估計沒法使用了,這UI和互動設計上都是業餘的顯然無法滿足大規模生產級應用的。但是按領域隔離後,或許在體驗上會有所改善。而且還有一點,目前開源版本的Dashboard只提供了最基本的登入驗證功能,如果你想要許可權控制、審計、審批確認等功能是需要二次開發的。如果Dashboard這塊按領域獨立了,在許可權控制這塊的風險性會更小。

當然,如果你想重構Dashboard許可權控制以及UI互動這些,我建議是按照應用維度來設計,加入基本的搜尋等。

其他的問題

應用在接入Sentinel後,需要啟動時指定應用名稱、Dashboard地址、客戶端的埠號、日誌配置、心跳設定等,要麼透過JVM -D啟動引數來實現,要麼在指定的路徑下存放配置檔案來配置。這都是不太合理的設計,對CI/CD和部署環境有侵入性,我在1.6.3版本時解決了這個問題並提交過PR,好在社群在1.7.0時解決了這個問題。

規則配置和使用上的一些經驗

請不要誤會,我不是教你怎麼配置怎麼使用,而是教你如何用好,還記得我在之前穩定性保障體系的文章中丟擲關於限流的靈魂拷問嗎?首先,我們簡單回顧下可能會用到的Sentinel中的關鍵功能。接下來我將以自問自答的方式解答使用者最常見的疑惑,輸出最有價值的經驗和建議。

1.    單機限流

2.    叢集限流

3.    閘道器限流

4.    熱點引數限流

5.    系統自適應保護

6.    黑白名單限制

7.    自動熔斷降級

單機限流閾值配多少?

這個不能“拍腦袋”,配太高了可能會引發故障,配太低了又擔心過早“誤殺”請求。還是得根據容量規劃和水位設定來配置,而且前提是監控告警靈敏。給出兩種比較實用的方式:

1.    參考單機容量規劃的思路,在軟負載中調整某個節點的流量權重和比例直到逼近極限為止。記錄極限狀態下的QPS,按照單機房70%的水位設定標準,你就可以推算出該資源的單機限流閾值了;

2.    你可以週期性觀察監控系統的流量圖,得到線上真實的峰值QPS,如果該週期內峰值時段應用系統和業務都是健康狀態的,那麼你可以假設該峰值QPS就是理論水位。這種方式是可能造成資源浪費的,因為峰值時段可能並未達到系統承載極限,適合流量週期性比較規律的業務;

你真的需要叢集限流嗎?

其實大多數場景下你並不需要使用叢集限流,單機限流就足夠了。仔細思考其實只有幾種情況下可能需要使用到叢集限流:

1.    當想要配置單機QPS限制 <1 時單機模式是無法滿足的,只能使用叢集限流模式來限制叢集的總QPS。比如有個效能極差的介面單機最多隻能扛住0.5QPS,部署了10臺機器那麼需要將叢集最大容量是5 QPS,當然這個例子有點極端。再比如我們希望某個客戶呼叫某個介面總的最大QPS為10,但是實際我們部署了20臺機器,這種情況是真實存在的;

2.    上圖中單機限流閾值是10 QPS,部署了3個節點,理論上叢集的總QPS可以達到30,但是實際上由於流量不均勻導致叢集總QPS還沒有達到30就已經觸發限流了。很多人會說這不合理,但我認為需要按真實情況來分析。如果這個 “10QPS”是根據容量規劃的系統承載能力推算出來的閾值(或者說該介面請求如果超過10 QPS就可能會導致系統崩潰),那這個限流的結果就是讓人滿意的。如果這個“10QPS”只是業務層面的限制,即便某個節點的QPS超過10了也不會導致什麼問題,其實我們本質上是想限制整個叢集總的QPS,那麼這個限流的結果就不是合理的,並沒有達到最佳效果;

所以,實際取決於你的限流是為了實現“過載保護”,還是實現業務層的限制。

還有一點需要說明的是:叢集限流並無法解決流量不均勻的問題,限流元件並不能幫助你重新分配或者排程流量。叢集限流只是能讓流量不均勻場景下整體限流的效果更好。

實際使用建議是:叢集限流(實現業務層限制)+ 單機限流(系統兜底,防止被打爆)

既然閘道器層已經限流了,那應用層還需要限流嗎?

需要的,雙重保護是很有必要。同理,上游的聚合服務配置了限流,下游的基礎服務也是需要配置限流的,試想下如果只配置了上游的限流,如果上游發起大量重試豈不是依舊可能壓垮下游的基礎服務?而且這種情況,我們在配置限流閾值時也需要特別注意,比如上游的A,B兩個服務都依賴了下游Y服務,A,B分別配置的100QPS,那麼Y服務至少得配置為200QPS,要不然有部分請求額外的經過透傳和處理但最終又被拒絕,不僅是浪費資源,嚴重了還可能導致資料不一致等問題。

所以,最好是根據全鏈路總體的容量規劃來配置(木桶短板原理),越早攔截越好,每一層都要配置限流。

熱點引數限流功能實用嗎?

功能挺實用的,可以防止熱點資料(比如:熱門店鋪、黑馬商品)佔用並消耗過多的系統資源,導致對其他資料的請求處理受到嚴重影響。

還有一種需求,如果你做C端的產品,你想限制某使用者訪問某介面的最大QPS,或者你是做B端的SAAS產品,你想限制某租戶訪問某介面的最大QPS…熱點引數預設不是為了滿足這類需求而設計的,你需要自行擴充套件SLOT去實現類似的限制需求。當然,熱點引數限流中的paramFlowItemList(引數例外項,可以實現指定某個客戶ID=1的大客戶訪問某資源的最大QPS為100),這在某種程度上是可以實現這種特殊需求的。對於這種需求還有一種解決辦法:我們在程式碼中定義resouceName時就直接給它賦予對應的業務資料標識(例如:queryAmount#{租戶Id}),然後根據resouceName去控制檯單獨配置。

為什麼還整出個系統自適應保護啊?

這個其實也是一種兜底的做法。當真實流量超過限流閾值一部分時,開銷基本可以忽略,當真實流量遠超限流閾值N倍時,尤其是像雙十一大促、春晚紅包、12306購票這種巨大流量的場景下,那麼限流拒絕請求的開銷就不能忽略了,這種情況在阿里內部稱為“系統被摸死”,這種場景下自適應限流可以做好兜底。

黑白名單限制需要配嗎?

如果你想根據請求來源做限制(僅放行指定上游系統過來的請求),那麼該功能非常有用的。Sentinel中內建了“簇點鏈路監控”功能,有點類似呼叫鏈監控但目的不一樣。

自動熔斷降級有啥使用建議?

配置自動熔斷降級前,首先我們需要識別出可能出現不穩定的服務,然後判斷其是否可降級。降級處理通常是快速失敗,當然我們業可以自定義降級處理結果(Fallback),例如:嘗試包裝返回預設結果(兜底降級),返回上一次請求的快取結果(時效性降級),包裝返回處理失敗的提示結果等。

對弱依賴和次要功能的降級通常是人工推送開關來完成的,而Sentinel的熔斷降級主要是在“呼叫端”自動判斷並執行的,Sentinel基於規則中配置的時間視窗內的平均響應時間、錯誤比例、錯誤數等統計指標來執行自動熔斷降級。

舉個例子:我們系統同時支援“餘額支付”和“銀行卡支付”,這兩個功能對應的介面預設在相同應用的同一執行緒池中,任何一方出現RT抖動和大量超時都可能請求積壓並導致執行緒池被耗盡。假設從業務角度來看“餘額支付”的比例更高,保障的優先順序也更高。那麼我們可以在檢查到 “銀行卡支付”介面(依賴第三方,不穩定)中RT持續上升或者發生大量異常時對其執行“自動熔斷降級”(前提是不能導致資料不一致等影響業務流程的問題),這樣優先保證“餘額支付”的功能可以繼續正常使用。

總結

本文主要介紹了Sentinel開源版在大規模生產級應用時所面臨的一些問題和解法,還有在實際配置使用時的一些經驗,這些經驗均來自一線生產實踐,希望能讓讀者朋友少走彎路。

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

相關文章