1、限流的策略
2、限流的算法:計(jì)數(shù)器、隊(duì)列、漏斗和令牌桶。
3、如何基于響應(yīng)時(shí)間來限流。
4、限流設(shè)計(jì)的要點(diǎn)
例:數(shù)據(jù)庫訪問連接池,線程池, nginx 下的用于限制瞬時(shí)并發(fā)連接數(shù)的 limit_conn 模塊,限制每秒平均速率 limit_req 模塊,限制 MQ 的生產(chǎn)速,等等。
一、限流的策略
拒絕服務(wù)。拒掉請求最多客戶端,把不正?;蚴菒阂獾母卟l(fā)訪問抵擋掉。
服務(wù)降級。(1)不重要的服務(wù)停掉, CPU、內(nèi)存或數(shù)據(jù)讓給重要功能;(2)不返回全量數(shù)據(jù),返回部分
全量數(shù)據(jù)需要做 SQL Join 操作,部分則不需要,所以可以讓 SQL 執(zhí)行更快,或直接返回預(yù)設(shè)的緩存,犧牲一致性,獲大吞吐。
特權(quán)請求。保大客戶的
延時(shí)處理。隊(duì)列緩沖請求,隊(duì)列滿了拒絕,如果這個(gè)隊(duì)列中的任務(wù)超時(shí)了,也要返回系統(tǒng)繁忙的錯(cuò)誤了。應(yīng)對短暫峰刺請求。
彈性伸縮。監(jiān)控系統(tǒng),感知忙 TOP 5服務(wù)
然后去伸縮它們,還需要一個(gè)自動化的發(fā)布、部署和服務(wù)注冊的運(yùn)維系統(tǒng),而且還要快,越快越好。否則,系統(tǒng)會被壓死掉了。db壓力大,彈性伸沒什么用,應(yīng)限流
二、限流的實(shí)現(xiàn)方式
1、計(jì)數(shù)器方式
一個(gè)請求來加一,請求處理完減一Counter 大于限流閾值,開始拒絕請求以保護(hù)系統(tǒng)的負(fù)載了。
2、隊(duì)列算法
請求速度波動的,處理速度均速。像 FIFO,先高優(yōu)先級,再處理低優(yōu)先級


低隊(duì)列被餓死,有帶權(quán)重的隊(duì)列。下圖:三個(gè)隊(duì)列的權(quán)重分布是 3:2:1,權(quán)重 3 的這個(gè)隊(duì)列上處理 3 個(gè)請求后,再去權(quán)重 2 的隊(duì)列上處理 2 個(gè)請求,最后去 1 的隊(duì)列上處理 1 個(gè)請求,返復(fù)。

隊(duì)列滿,觸發(fā)限流:用隊(duì)列長度來控制流量,配置上難操作。過長導(dǎo)致沒滿就掛掉了。不能做 push ,pull 方式會好一些
3、漏斗算法 Leaky Bucket
像漏斗一樣,漏斗中水太多,溢出
實(shí)現(xiàn):隊(duì)列請求 + 限流器,讓 Processor 勻速處理
如?TCP。請求過多, sync backlog 隊(duì)列緩沖請求,或TCP 滑動窗口


4、令牌桶算法 Token Bucket
有中間人。在桶內(nèi)按照一定速率放入token,拿到 token,才能處理

漏斗算法中,處理請求是以一個(gè)常量和恒定的速度處理的,而令牌桶算法則是在流量小的時(shí)候“攢錢”,流量大的時(shí)候,快速處理。
如Processor 像 Nginx 沒什么邏輯,速度快,沒瓶頸,而把請求轉(zhuǎn)給后端,這兩個(gè)算法就不一樣了:
? ??漏斗算法:穩(wěn)定的速度轉(zhuǎn)發(fā)
? ??令牌桶:流量大時(shí),一次發(fā)出。還可做第三方服務(wù),對全局進(jìn)行流控
三、基于響應(yīng)時(shí)間動態(tài)限流
1、服務(wù)會依賴于數(shù)據(jù)庫。數(shù)據(jù)不斷片花
2、不同 API 不同性能。
3、自動化伸縮情況下,不同大小集群的性能也不一樣,動態(tài)地調(diào)整太難
因?yàn)?23 很難設(shè)定限流值,感知系統(tǒng)壓力,來自動化地限流
典范:?TCP 協(xié)議擁塞控制算法。TCP 使用 RTT - Round Trip Time 來探測網(wǎng)絡(luò)的延時(shí)和性能,設(shè)定相應(yīng)“滑動窗口”的大小,發(fā)送速率和網(wǎng)絡(luò)性能相匹配。
記錄每次調(diào)用后端請求的響應(yīng)時(shí)間,在時(shí)間區(qū)間內(nèi)(如過去 10 秒)請求計(jì)算響應(yīng)時(shí)間 P90 或 P99 值,過去 10 秒請求響應(yīng)時(shí)間排序,看 90% 或 99% 位置多少(超過設(shè)定閾值,自動限流)
設(shè)計(jì)中要點(diǎn):
計(jì)算的一定時(shí)間內(nèi)的 P90 或 P99。大量請求下,非常地耗內(nèi)存和 CPU,因?yàn)橐獙Υ罅繑?shù)據(jù)排序。
解決方案有兩種,一種是不記錄所有的請求,采樣就好了,另一種是使用一個(gè)叫蓄水池的近似算法。關(guān)于這個(gè)算法這里我不就多說了,《編程珠璣》里講過這個(gè)算法,你也可以自行 Google,英文叫Reservoir Sampling。
這種動態(tài)流控需要像 TCP 那樣,你需要記錄一個(gè)當(dāng)前的 QPS. 如果發(fā)現(xiàn)后端的 P90/P99 響應(yīng)太慢,那么就可以把這個(gè) QPS 減半,然后像 TCP 一樣走慢啟動的方式,直接到又開始變慢,然后減去 1/4 的 QPS,再慢啟動,然后再減去 1/8 的 QPS……
這個(gè)過程有點(diǎn)像個(gè)阻尼運(yùn)行的過程,然后整個(gè)限流的流量會在一個(gè)值上下做小幅振動。這么做的目的是,后端擴(kuò)容伸縮后性能變好,自動適應(yīng)后端的最大性能。
四、限流的設(shè)計(jì)要點(diǎn)
限流目的:
1、為了向用戶承諾 SLA。保證某個(gè)速度下的響應(yīng)時(shí)間以及可用性。
2、阻止多租戶的情況下,某一用戶把資源耗盡,而讓所有的用戶都無法訪問的問題。
3、應(yīng)對突發(fā)的流量。
4、節(jié)約成本。不為不常見的尖峰擴(kuò)容到最大的尺寸。有限的資源下能夠承受比較高的流量。
設(shè)計(jì)上的考量:
1、早期考慮。當(dāng)架構(gòu)形成后,限流不是很容易加入。
2、限流模塊必需是非常好的性能,而且對流量的變化也是非常靈敏的,否則太過遲鈍的限流,系統(tǒng)早因?yàn)檫^載而掛掉了。
3、手動的開關(guān),應(yīng)急。
4、監(jiān)控事件通知。運(yùn)維人員可以及時(shí)跟進(jìn)。還可以自動化觸發(fā)擴(kuò)容或降級。
5、返回限流錯(cuò)誤碼。和其它錯(cuò)誤區(qū)分開來??蛻舳丝吹较蘖?,可以調(diào)整發(fā)送速度,或走重試機(jī)制。
6、讓后端的服務(wù)感知到。比如 HTTP Header 中,放入一個(gè)限流的級別,告訴后端服務(wù)目前正在限流中。這樣,后端服務(wù)可以根據(jù)標(biāo)識決定是否做降級。
也歡迎你分享一下你實(shí)現(xiàn)過怎樣的限流機(jī)制?