限流
限流,顧名思義,限制系統(tǒng)的流量,防止用戶過多地訪問系統(tǒng)的資源,甚至是惡意地訪問,比如惡意爬蟲,DDOS 等;同時也防止系統(tǒng)承載過多流量而崩潰,從而對系統(tǒng)運行資源做到一個有效的管理
在分布式系統(tǒng)中,節(jié)點之間需要相互調(diào)用,如果調(diào)用鏈中一個節(jié)點宕機,將會導(dǎo)致整個鏈路都無法訪問,從而造成雪崩問題,使整個系統(tǒng)出現(xiàn)不可用狀態(tài),為了解決這種問題,保持高可用的性質(zhì),常見處理方式有:超時返回,調(diào)用方調(diào)用超時直接返回不再無效等待;艙壁模式,限定每個業(yè)務(wù)能使用的線程數(shù),當(dāng)多個線程阻塞于同一個服務(wù),后續(xù)的線程將不能調(diào)用該服務(wù),也就不會被阻塞;熔斷降級,將被調(diào)用的業(yè)務(wù)進行熔斷降級;以及 限流。
前三種更多是在節(jié)點宕機被發(fā)現(xiàn)后的應(yīng)對措施,而限流更偏向于一種預(yù)防措施
限流規(guī)則
- QPS 和連接數(shù)
- 傳輸速率:限制用戶下載,訪問某些資源的傳輸速率
- 黑白名單:將不同 IP 添加到黑白名單,實現(xiàn) IP 維度的限流策略
限流方案
分布式系統(tǒng)中,主流的限流方案有兩種:
- 網(wǎng)關(guān)限流:將限流操作設(shè)置在所有流量的入口處,通常是網(wǎng)關(guān)
- 獨立中間件限流,將限流服務(wù)部署到單獨的某臺服務(wù)器上,其它節(jié)點都通過從這里獲取限流信息然后確定允許流量或者拒絕流量
限流算法
計數(shù)限流
計數(shù)限流應(yīng)該是最簡單的限流算法。例如系統(tǒng)限制同時只能處理 100 個請求,那么就維護一個計數(shù)器,接受一個請求時計數(shù)器加一,處理完畢時計數(shù)器減一,且每次請求到來時先判斷計數(shù)器的值是否超出閾值,是的話拒絕請求
根據(jù)系統(tǒng)是單體系統(tǒng)還是分布式系統(tǒng)又可細(xì)分為單機限流跟分布式限流。單機限流的話可以使用 Java 中的原子整數(shù)作為計數(shù)器;分布式限流的可以使用 Redis 中的 incr 命令
計數(shù)限流算法的缺陷在于,只考慮了流量的閾值,沒有考慮流量的突發(fā)性。一小時達到 100 個請求的壓力跟 1 秒內(nèi)達到 100 個請求的壓力是完全不同的,后者這種突發(fā)性的流量對系統(tǒng)的壓力是非常大的,因此需要限制一定時間間隔內(nèi)的流量大小,引入時間窗口,比如最簡單的 固定窗口限流
固定窗口
固定窗口算法相比于傳統(tǒng)計數(shù)限流多了一個時間窗口的概念,計數(shù)器在每個固定的時間窗口內(nèi),如果計數(shù)器的值小于請求閾值,就允許請求訪問,同時計數(shù)器增一;否則就不允許。每經(jīng)過一個時間窗口的長度計數(shù)器就重置,進入到下一個時間窗口
這種算法看起來確實可以確保每個窗口內(nèi)的請求數(shù)不超出閾值,但卻不能確保兩個窗口之間的交界區(qū)域中的限流,比如閾值設(shè)置為 每個窗口為 1 s,然后每秒最多 100 個請求,那么如果在第 i 個窗口的后半段有 51 個請求,進入到第 i + 1 個窗口時計數(shù)器重置,在第 i + 1 個窗口的前半段有 51 個請求,那么根據(jù)算法,這 102 個請求都是會被允許的,但是在第 i 個窗口的后半段跟第 i + 1 個窗口的前半段組成的這個同樣為 1s 的時間窗口中卻有 102 個請求被允許,不符合限流的閾值要求
所以時間窗口不能固定,需要使用滑動窗口
滑動窗口
滑動窗口中不需要記錄每次時間窗口的起始邊界,而是在每個請求到來時,根據(jù)時間戳來減去時間窗口長度,比如 1 s,然后動態(tài)地得到窗口的邊界,再判斷到達時間戳處于該窗口內(nèi)的請求數(shù)是否超出閾值,從而確定允許或者拒絕該請求。所以該算法需要保存每個請求的到達時間戳,同時需要清除掉窗口長度之前的過時請求的到達時間戳,如果一個窗口長度內(nèi)的請求數(shù)很多,需要花費一定的內(nèi)存存儲開銷
這種方法的問題在于:
- 計算機的時鐘受硬件限制,在時間計算上可能存在誤差,無法滿足較高的時效性要求;
- 第二是該方法只能滿足一定長度的時間窗口內(nèi)的限流,不能限制集中在極短時間間隔內(nèi)的流量爆發(fā),因為系統(tǒng)的時間精度可能達不到要求,不能確保流量平滑。而且有的時候資源的限制條件是有多個的,比如 1 s 內(nèi)不超出 100 個請求,且為了抵御高并發(fā),10 ms 內(nèi)不超出 5 個請求,這樣單個時間窗口的限制就無法滿足需求。當(dāng)然,我們可以通過同時設(shè)置多個窗口同時進行計數(shù)來達到多限制條件的目的
漏桶算法
為了解決流量突發(fā)導(dǎo)致流量不平滑的問題,我們設(shè)置一個漏桶,當(dāng)請求到來時不是直接判斷允許或者拒絕,而是先放到桶中,如果桶內(nèi)存放的請求數(shù)量達到桶的容量了再拒絕后續(xù)的請求,然后漏桶定時地將桶內(nèi)的請求放出,由后端服務(wù)拿去處理
可以看出,在這種算法中,無論請求產(chǎn)生的速率多大,后端服務(wù)拿去處理的速率都是固定的,從而使流量平滑,跟消息隊列很類似,削峰填谷。在這里,計算機中的一大定理 —— 一切問題都能通過增加一個中間層來解決,再一次發(fā)揮作用
但是 絕對的流量平滑并不一定是好事。有些突發(fā)請求,我們是可以接受的,因為需要為了滿足用戶的體驗而盡快處理,只要在系統(tǒng)可以平穩(wěn)運行的前提下即可。為此,需要令牌桶算法
令牌桶算法
令牌桶算法中同樣需要一個漏桶,但放入桶中的不是請求了,而是令牌。令牌會定時地放入漏桶,如果桶中令牌數(shù)量超出桶容量,則后續(xù)的令牌被丟棄。當(dāng)有請求到來時,需要先向桶索取令牌,索取成功則被允許可以處理,否則被拒絕。這個思路跟 信號量 很類似,可以控制某種資源被同時訪問的對象數(shù)目
當(dāng)多個請求突發(fā)時,假設(shè)桶內(nèi)有充足的令牌,那么這些突發(fā)的請求都可以馬上獲取令牌然后被處理,而不像漏桶算法那樣只能以永遠(yuǎn)固定的速率被處理,所以在應(yīng)對突發(fā)流量時,令牌桶算法的表現(xiàn)更佳
令牌桶算法有個問題就是,在系統(tǒng)剛開始運行時,桶中是沒有令牌的,那么一開始的請求就獲取不到足夠的令牌,無法被處理,但系統(tǒng)剛開始運行時應(yīng)該是有充足的資源來處理請求的。處理方案就是一開始應(yīng)該進行令牌桶的 預(yù)熱,預(yù)先放入幾個令牌,確保系統(tǒng)剛開始運行時的請求能及時被處理
相關(guān)組件
Hystrix
Hystrix 是 SpringCloud 框架中的一個組件,可以使用信號量跟線程池來進行限流
Nginx
Nginx 優(yōu)秀的代理,路由和負(fù)載均衡功能使其成為網(wǎng)關(guān)中的首選,而且它也提供了限流的功能,因此選擇網(wǎng)關(guān)限流的話可以使用 Nginx。Nginx 提供了兩種限流方法,分別是控制速率以及控制并發(fā)連接數(shù),采用的算法是漏桶算法
Sentinel
Sentinel 是阿里開源的用于服務(wù)容錯的綜合性解決方案,支持限流熔斷降級等功能
對資源的 限流閾值 可以設(shè)置為 QPS 或者 每秒的線程數(shù)
默認(rèn)的 限流模式 為 直接模式,限流效果為快速失敗。此外還支持其它限流模式,如關(guān)聯(lián)模式和鏈路模式。
在 關(guān)聯(lián)模式 中,對于某個資源,可以設(shè)置它的關(guān)聯(lián)資源,當(dāng)它的關(guān)聯(lián)資源達到限流閾值時,它自己就會被限流。這種操作適用于為了確保某些重要資源可用而限制其它資源的 應(yīng)用讓步 的場景,例如訂單服務(wù)有讀取訂單信息跟寫入訂單信息兩個接口,在高并發(fā)的場景下,可能兩個接口都會占用系統(tǒng)資源,這種情況下我們可能想優(yōu)先保障寫入信息接口的使用,那就可以使用關(guān)聯(lián)模式,對讀取信息接口開啟關(guān)聯(lián)模式,設(shè)置關(guān)聯(lián)資源為寫入信息接口,這樣當(dāng)寫入信息接口的請求增多,讀取信息的接口就會被限制,從而使系統(tǒng)資源傾向于寫入信息接口,確保寫入信息接口的可用。
鏈路模式 基于指定鏈路的入口進行限流,不同的資源接口可能有不同的或相同的入口,對某個入口的限流不會影響在其它入口進入的請求
限流效果 除了 快速失敗 還有 Warm Up 和 排隊等待。
Warm Up 主要應(yīng)對于系統(tǒng)長期處于低水位狀態(tài)下,遇到流量突然暴增,直接把系統(tǒng)抬高到高水位使得系統(tǒng)有可能被壓垮的情況。通過冷啟動和預(yù)熱,避免冷系統(tǒng)被壓垮?;诘氖橇钆仆八惴ā>唧w到實現(xiàn)來說,如果某個資源選擇的是 Warm Up 的限流效果,那么請求的閾值一開始會是初始閾值除以一個冷卻因子,默認(rèn)為 3,即 一開始的閾值只有用戶設(shè)置的閾值的三分之一,然后 經(jīng)過預(yù)熱時長,把閾值逐漸升至設(shè)定的閾值,從而完成冷啟動。
排隊等待主要用于嚴(yán)格限制請求通過系統(tǒng)的速率,以勻速方式經(jīng)過,對應(yīng)的是漏桶算法
熔斷
我們知道,在電路中,保險絲用于保護電路,當(dāng)電路電流過大時,保險絲就會熔斷,從而避免器件損壞。而應(yīng)用系統(tǒng)中的熔斷也類似如此。服務(wù)熔斷是指調(diào)用方訪問服務(wù)時通過一個斷路器作為代理進行調(diào)用,而斷路器會持續(xù)觀察被調(diào)用服務(wù)返回的狀態(tài)是成功亦或是失敗,當(dāng)失敗次數(shù)超過設(shè)置的閾值時斷路器打開,請求就不能到達服務(wù)了,從而避免調(diào)用方阻塞于調(diào)用過程
斷路器的狀態(tài)
斷路器有三種狀態(tài):文章來源:http://www.zghlxwxcb.cn/news/detail-701600.html
- CLOSED:默認(rèn)狀態(tài),斷路器觀察到被調(diào)用服務(wù)請求失敗比例沒有達到閾值,認(rèn)為被代理服務(wù)狀態(tài)良好
- OPEN:斷路器觀察到請求失敗的比例已經(jīng)達到閾值,于是認(rèn)為被代理服務(wù)故障,打開開關(guān),使請求不再到達被代理服務(wù),而是快速失敗
- HALF OPEN:斷路器打開后后續(xù)需要嘗試恢復(fù)對被代理服務(wù)的訪問,此時需要切換到半打開狀態(tài),然后去請求被代理服務(wù)以查看服務(wù)是否已經(jīng)恢復(fù)正常。如果確認(rèn)服務(wù)已經(jīng)恢復(fù)正常,則斷路器轉(zhuǎn)為 CLOSED 狀態(tài),否則轉(zhuǎn)到 OPEN 狀態(tài)
需要考慮的問題
- 熔斷的時長設(shè)置為多長,即超過這個時長后切換到 HALF OPEN 進行重試
- 針對不同的異常,可能需要定義不同的熔斷后處理邏輯
- 要記錄請求失敗日志,供監(jiān)控使用
- 不一定要等到熔斷時長過后才進行重試,可以考慮主動重試,比如對于 connection timeout 這種有可能短期內(nèi)恢復(fù)的問題而造成的熔斷,可以用異步線程進行網(wǎng)絡(luò)檢測,比如 telenet,檢測到網(wǎng)絡(luò)暢通時切換到 HALF OPEN 進行重試
- 設(shè)置 補償接口,讓運維人員可以手工關(guān)閉斷路器。
- 重試時,可以使用之前失敗的請求進行重試,但一定要注意業(yè)務(wù)上是否允許這樣做
降級
在服務(wù)被熔斷后,一般會讓后續(xù)的請求走事先配置好的處理方法,這個處理方法就是一個降級邏輯。通常是在系統(tǒng)高并發(fā)時,為了使重要的核心業(yè)務(wù)正常運行,對非核心,非關(guān)鍵的業(yè)務(wù)不再讓其正常地占有部分資源,進行降級處理,從而讓出系統(tǒng)資源給核心業(yè)務(wù)執(zhí)行文章來源地址http://www.zghlxwxcb.cn/news/detail-701600.html
到了這里,關(guān)于[分布式]-限流熔斷降級的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!