題圖:
前言
互聯網上關于限流算法、功能介紹、基本結構、原理分析的文章可謂汗牛充棟,我并不打算重複制造内容。我将為大家分享在實際工作和生産環境中使用、踩坑的經驗。
如果你正在做限流、熔斷的技術選型,那麼本文将會為你提供客觀、有價值的參考;
如果你将來要在生産環境中使用,那麼本文将會幫助你後續少走彎路;
如果你正在準備求職面試,或許可以幫你的技能樹和經驗上增加亮點,避免你的面試評價表上被人寫上“紙上談兵”;
開源版和阿裡内部的是一樣的嗎?我們可以在大規模生産級應用嗎?
這裡我先直接告訴大家答案:開源的和内部版本是一樣的,最核心的代碼和能力都開源出來了。可以生産級應用,但并非 “開箱即用”,需要你做一些二次開發和調整,接下來我會對這些問題仔細展開。當然,我更推薦你直接使用阿裡雲上的AHAS 控制台和ASM配置中心,那是最佳實踐的輸出,你可以節省很多時間、人力、運維成本等。
總體運行架構
大規模生産級應用面臨的問題
看完開源版原始的運行架構,很明顯其中存在一些問題:
限流、降級等規則保存在應用節點的内存中,應用發布重啟後就會失效,這在生産環境中顯然是無法接受的;規則下發默認是按照機器節點維度的而非按應用維度,而正常公司的應用系統都是集群部署的,而且這樣也無法支持集群限流;信息由拉取上來後保存到内存中,僅僅保留5分鐘,錯過後可能無法還原 “案發現場”,而且無法看到流量趨勢;如果接入限流的應用有500+個,每個應用平均部署4個節點,那麼總共2000個節點,那麼肯定會成為瓶頸,單機的線程池根本處理不過來; 如何優化并解決這些問題
接下來,我們就先一一的介紹如何解決上述這些明顯的問題。
首先,限流規則、降級規則等都應該按照應用維度去下發,而不是按照APP單節點的維度。因為支持集群限流,所以開源版本的 在針對限流規則這塊已經做了擴展,但對于熔斷、系統保護等還未擴展支持按應用維度下發,感興趣的讀者可以參考 的實現去實現。
其次,規則不應該保存内存中,應該持久化到動态配置中心,而應用直接從配置中心訂閱規則即可。這樣,和應用就通過配置中心實現解耦了,這是典型的生産者消費者模式,基本運行架構如下:
以nacos配置中心為例,官方和社區提供了限流規則保存和訂閱的Demo,接下來熔斷降級、系統保護、網關限流…等規則你其實都可以“照貓畫虎”的去擴展。基本模式就是:将 VO模型序列化後保存到nacos,應用從nacos訂閱後反序列成領域模型。
這裡我特别提醒一下各位前方有巨坑,“熱點參數限流規則”和“黑名單限制規則”這塊請勿直接照搬,因為 中定義的、
這2個VO模型和領域模型、中的字段定義不匹配,會導緻序列化/反序列化失敗的問題,繼而導緻應用無法訂閱和使用熱點參數限流規則和黑名單限制規則,這塊我會提交PR!!!
第3點,中有個調度線程池,會輪詢方式請求(默認是每隔1秒鐘發起)各應用的機器節點查詢日志信息,聚合後并在界面上做監控展示(改造後還需完成持久化動作)。這是典型的pull模式,是目前監控度量領域比較通用的架構。因為是保存内存中所以默認僅保留5分鐘,這也是有問題的。推薦有如下幾種解法:
在拉取到信息後,直接保存到時序數據庫中,自身也從時序數據庫中取數據展示。數據存多久,這個你自己根據業務來決定。以開源的為例其自帶持久策略功能(數據過期自動清理)。并且,你還可以借助等開源做查詢、聚合,展示各種漂亮的大盤、圖形、排行榜等;你可以将pull的模式改為push模式,在記錄日志時改為直接寫時序數據庫,當你,基于性能的考慮你也可以改為寫MQ做緩沖。除了耗時,最關鍵的是不能因為這個記錄的動作影響到主業務流程的推進;繼續打印日志,啟用 拉取數據,改用直接在應用機器節點上通過采集器對日志做采集、處理、上報,可以借助ELK等工具;你可以嘗試自己開發 ,自己将信息以形式暴露出去,由服務端定時去拉取,同時你也可以使用提供的各種豐富的查詢、聚合的語法和能力,通過等做展示;
下圖是典型的時序數據的例子,天生就是為指标數據而設計的,該領域比較知名的開源軟件有、等。
限流大盤展示效果圖
以上方式各有優劣,如果你想改動最小,并且你們應用接入和部署規模并不是特别大(500節點以内),那麼請選擇第1種方式。
第4點,關于接入應用和節點較多導緻在拉取、聚合時的性能瓶頸。在解決問題3的時候,如果你選擇了2,3,4這幾種方法,那麼自帶的将僅僅作為規則下發的工具(甚至規則下發都可以直接通過nacos配置中心的控制台完成),自然就不會有瓶頸的問題了。如果你依舊想借助自帶的來完成數據的拉取和持久化等任務,那麼我提供給你有兩個解法:
按領域隔離,不同業務域的應用連接到各自的 上,這樣自然就分攤壓力減少瓶頸出現的可能性了。優點就是幾乎無需做改造,缺點就是顯得不統一;你可以嘗試改造自帶的,讓其具備無狀态性。前面我們提過,應用端啟動後會定時上報心跳信息,這邊默認會在内存中維護一份 “節點信息列表”的數據,這個是典型的狀态性數據,應該考慮放到集中存儲中,例如:redis中。然後你需要改造“拉取信息”的線程池,改為分片任務方式去執行,這樣達到分攤負載的作用,例如:改為使用調度。當然,時序數據庫的寫入也是有可能成為瓶頸的;你可以犧牲一點監控指标的時效性,将 中調度線程池的間隔時間參數調大一點,這樣可以緩解下遊工作線程池的處理壓力;
就我個人而言其實更推薦第1,3這兩種方式,這都是改動比較小的權宜之計。
當然,按領域劃分其實也是有其他好處的。你試想如果接入了500+系統,以目前開源版的為例,左側應用列表得拉多長?估計沒法使用了,這UI和交互設計上都是業餘的顯然無法滿足大規模生産級應用的。但是按領域隔離後,或許在體驗上會有所改善。而且還有一點,目前開源版本的隻提供了最基本的登錄驗證功能,如果你想要權限控制、審計、審批确認等功能是需要二次開發的。如果這塊按領域獨立了,在權限控制這塊的風險性會更小。
當然,如果你想重構權限控制以及UI交互這些,我建議是按照應用維度來設計,加入基本的搜索等。如果
其他的問題
應用在接入後,需要啟動時指定應用名稱、地址、客戶端的端口号、日志配置、心跳設置等,要麼通過JVM -D 啟動參數來實現,要麼在指定的路徑下存放配置文件來配置。這都是不太合理的設計,對CI/CD和部署環境有侵入性,我在1.6.3版本時解決了這個問題并提交過PR,好在社區在1.7.0時解決了這個問題。
規則配置和使用上的一些經驗
請不要誤會,我不是教你怎麼配置怎麼使用,而是教你如何用好,還記得我在之前穩定性保障體系的文章中抛出關于限流的靈魂拷問嗎?首先,我們簡單回顧下可能會用到的中的關鍵功能。接下來我将以自問自答的方式解答使用者最常見的疑惑,輸出最有價值的經驗和建議。
單機限流集群限流網關限流熱點參數限流系統自适應保護黑白名單限制自動熔斷降級單機限流阈值配多少?
這個不能“拍腦袋”,配太高了可能會引發故障,配太低了又擔心過早“誤殺”請求。還是得根據容量規劃和水位設定來配置,而且前提是監控告警靈敏。給出兩種比較實用的方式:
參考單機容量規劃的思路,在軟負載中調整某個節點的流量權重和比例直到逼近極限為止。記錄極限狀态下的QPS,按照單機房70%的水位設定标準,你就可以推算出該資源的單機限流阈值了;你可以周期性觀察監控系統的流量圖,得到線上真實的峰值QPS,如果該周期内峰值時段應用系統和業務都是健康狀态的,那麼你可以假設該峰值QPS就是理論水位。這種方式是可能造成資源浪費的,因為峰值時段可能并未達到系統承載極限,适合流量周期性比較規律的業務; 你真的需要集群限流嗎?
其實大多數場景下你并不需要使用集群限流,單機限流就足夠了。仔細思考其實隻有幾種情況下可能需要使用到集群限流:
當想要配置單機QPS限制 < 1 時單機模式是無法滿足的,隻能使用集群限流模式來限制集群的總QPS。比如有個性能極差的接口單機最多隻能扛住0.5 QPS,部署了10台機器那麼需要将集群最大容量是5 QPS,當然這個例子有點極端。再比如我們希望某個客戶調用某個接口總的最大QPS為10,但是實際我們部署了20台機器,這種情況是真實存在的;上圖中單機限流阈值是10 QPS,部署了3個節點,理論上集群的總QPS可以達到30,但是實際上由于流量不均勻導緻集群總QPS還沒有達到30就已經觸發限流了。很多人會說這不合理,但我認為需要按真實情況來分析。如果這個 “10QPS”是根據容量規劃的系統承載能力推算出來的阈值(或者說該接口請求如果超過10 QPS就可能會導緻系統崩潰),那這個限流的結果就是讓人滿意的。如果這個“10 QPS”隻是業務層面的限制,即便某個節點的QPS超過10了也不會導緻什麼問題,其實我們本質上是想限制整個集群總的QPS,那麼這個限流的結果就不是合理的,并沒有達到最佳效果;
所以,實際取決于你的限流是為了實現“過載保護”,還是實現業務層的限制。
還有一點需要說明的是:集群限流并無法解決流量不均勻的問題,限流組件并不能幫助你重新分配或者調度流量。集群限流隻是能讓流量不均勻場景下整體限流的效果更好。
實際使用建議是:集群限流(實現業務層限制)+ 單機限流(系統兜底,防止被打爆)
既然網關層已經限流了,那應用層還需要限流嗎?
需要的,雙重保護是很有必要。同理,上遊的聚合服務配置了限流,下遊的基礎服務也是需要配置限流的,試想下如果隻配置了上遊的限流,如果上遊發起大量重試豈不是依舊可能壓垮下遊的基礎服務?而且這種情況,我們在配置限流阈值時也需要特别注意,比如上遊的A,B兩個服務都依賴了下遊Y服務,A,B分别配置的100 QPS,那麼Y服務至少得配置為200 QPS,要不然有部分請求額外的經過透傳和處理但最終又被拒絕,不僅是浪費資源,嚴重了還可能導緻數據不一緻等問題。
所以,最好是根據全鍊路總體的容量規劃來配置(木桶短闆原理),越早攔截越好,每一層都要配置限流。
熱點參數限流功能實用嗎?
功能挺實用的,可以防止熱點數據(比如:熱門店鋪、黑馬商品)占用并消耗過多的系統資源,導緻對其他數據的請求處理受到嚴重影響。
還有一種需求,如果你做C端的産品,你想限制某用戶訪問某接口的最大QPS,或者你是做B端的SAAS産品,你想限制某租戶訪問某接口的最大QPS…熱點參數默認不是為了滿足這類需求而設計的,你需要自行擴展SLOT去實現類似的限制需求。當然,熱點參數限流中的(參數例外項,可以實現指定某個客戶ID=1的大客戶訪問某資源的最大QPS為100),這在某種程度上是可以實現這種特殊需求的。對于這種需求還有一種解決辦法:我們在代碼中定義時就直接給它賦予對應的業務數據标識(例如:#{租戶Id}),然後根據去控制台單獨配置。
為什麼還整出個系統自适應保護啊?
這個其實也是一種兜底的做法。當真實流量超過限流阈值一部分時,開銷基本可以忽略,當真實流量遠超限流阈值N倍時,尤其是像雙十一大促、春晚紅包、12306購票這種巨大流量的場景下,那麼限流拒絕請求的開銷就不能忽略了,這種情況在阿裡内部稱為“系統被摸死”,這種場景下自适應限流可以做好兜底。
黑白名單限制需要配嗎?
如果你想根據請求來源做限制(僅放行指定上遊系統過來的請求),那麼該功能非常有用的。中内置了“簇點鍊路監控”功能,有點類似調用鍊監控但目的不一樣。
自動熔斷降級有啥使用建議?
配置自動熔斷降級前,首先我們需要識别出可能出現不穩定的服務,然後判斷其是否可降級。降級處理通常是快速失敗,當然我們業可以自定義降級處理結果(),例如:嘗試包裝返回默認結果(兜底降級),返回上一次請求的緩存結果(時效性降級),包裝返回處理失敗的提示結果等。
對弱依賴和次要功能的降級通常是人工推送開關來完成的,而的熔斷降級主要是在“調用端”自動判斷并執行的,基于規則中配置的時間窗口内的平均響應時間、錯誤比例、錯誤數等統計指标來執行自動熔斷降級。
舉個例子:我們系統同時支持“餘額支付”和“銀行卡支付”,這兩個功能對應的接口默認在相同應用的同一線程池中,任何一方出現RT抖動和大量超時都可能請求積壓并導緻線程池被耗盡。假設從業務角度來看“餘額支付”的比例更高,保障的優先級也更高。那麼我們可以在檢查到 “銀行卡支付”接口(依賴第三方,不穩定)中RT持續上升或者發生大量異常時對其執行“自動熔斷降級”(前提是不能導緻數據不一緻等影響業務流程的問題),這樣優先保證“餘額支付”的功能可以繼續正常使用。
總結
本文主要介紹了開源版在大規模生産級應用時所面臨的一些問題和解法,還有在實際配置使用時的一些經驗,這些經驗均來自一線生産實踐,希望能讓讀者朋友少走彎路。如有疑問,歡迎留言讨論。
作者介紹
步崖,曾就職于阿⾥巴巴和螞蟻金服。熟悉⾼并發、⾼可用架構,穩定性保障等。 熱衷 于技術研究和分享,發表過”分布式事務”、”分布式緩存”等多篇⽂章被⼴泛閱讀和轉載
上一篇
寫詩寫詞常用的詞語和對仗
有話要說...