1 背景
限流是生產(chǎn)中經(jīng)常遇到的一個(gè)場(chǎng)景, 目前現(xiàn)有的一個(gè)工具大部分是提供單機(jī)限流的能力, 例如 google 的 guava 中提供的 RateLimiter. 但是生產(chǎn)環(huán)境大部分是分布式環(huán)境, 在多臺(tái)機(jī)器的環(huán)境下, 需要的是能對(duì)多臺(tái)機(jī)器一起限流的分布式限流。 分布式限流依賴公共的后端存儲(chǔ), 所以還需要自己搭建。
2 算法
說(shuō)到限流, 首先依賴的是限流的算法, 限流的算法很多包括令牌桶, 漏桶。
滑動(dòng)窗口
滑動(dòng)窗口算法的優(yōu)點(diǎn)在于可以在滑動(dòng)時(shí)間內(nèi)計(jì)算出相對(duì)精確的限流數(shù)據(jù)。 想象一個(gè)簡(jiǎn)單的限流算法, 例如限制在一分鐘內(nèi)最多訪問(wèn) 10 次。 我們?cè)诤蠖舜鎯?chǔ)的結(jié)構(gòu)如下
假設(shè)每個(gè)框代表了一分鐘, 框中存儲(chǔ)了一分鐘內(nèi)的限流數(shù)據(jù), 那么問(wèn)題在于我們想要紅色框的限流的數(shù)據(jù)時(shí)將無(wú)法計(jì)算, 也就是說(shuō)我們的限流的時(shí)間節(jié)點(diǎn)的起止時(shí)間是固定的。 而滑動(dòng)窗口之所以為“滑動(dòng)”, 則是為了解決這個(gè)問(wèn)題誕生。 而實(shí)際上, 這個(gè)方法也是令牌桶的一種變相實(shí)現(xiàn)。
滑動(dòng)實(shí)現(xiàn)的核心思想, 在于將時(shí)間塊切分到更細(xì)的精度, 假如我們繼續(xù)將 1 分鐘切分為更小的維度, 例如 5 秒, 那么以后我們的頻率計(jì)算的時(shí)間節(jié)點(diǎn)就可以變得更精確, 例如 10:11:00 ~ 10:12:00, 10:11:05 ~ 10:12:05, 10:11:10 ~ 10:12:10 。。。 做到 5 秒的精度, 如圖
從另一個(gè)角度來(lái)講, 一分鐘時(shí)間的間隔, 實(shí)際上也是一種滑動(dòng)的特殊情況, 只不過(guò)精度一分鐘。
3 后端存儲(chǔ)
既然說(shuō)到分布式實(shí)現(xiàn), 則需要考慮公共的后端存儲(chǔ)服務(wù), 此處我們選擇 redis, 因?yàn)?redis 提供了方便的數(shù)據(jù)結(jié)構(gòu)供我們實(shí)現(xiàn)滑動(dòng)窗口, 主要會(huì)用到 redis 中的 map. 具體實(shí)現(xiàn)可以參照代碼。
實(shí)現(xiàn)
為了保證單次限流各種操作的原子性, 我們選擇使用 lua 腳本執(zhí)行限流邏輯, 最終會(huì)返回是否達(dá)到流量限制的結(jié)果。
key : 限流記錄的 key, 此處的 key 由外部傳入, 一般根據(jù)我們需要限流的維度來(lái)生成。 例如如果是按 ip 對(duì)某個(gè) url 做訪問(wèn)限流限制, 則 key 可能是 url:/test:ip:192.168.1.1
current time : 當(dāng)前時(shí)間, 使用服務(wù)端 redis 時(shí)間, 為了保證分布式情況下時(shí)間的一致性, 這里的使用通過(guò) redis.time 獲取并傳入 lua 腳本
duration : 限流的總時(shí)長(zhǎng), 例如 1 分鐘則是 60 * 1000 ms
limitation : 最高流量限制, 例如每分鐘 10 次, 則為 10
precision : 限流精度, 例如精度是 1s, 則為 1000 ms, 限流精度也是保證能實(shí)現(xiàn)上圖紅框內(nèi)限流的關(guān)鍵, 精度越小, 限流越精確, block 數(shù)也越多, 占用的內(nèi)存也越大。 實(shí)際上上圖的簡(jiǎn)單限流即是 duration = precision 的一種特殊情況
permits : 本次需要增加多少流量, 對(duì)于頻率來(lái)說(shuō)一般是 1, 而對(duì)于流量來(lái)說(shuō)則是數(shù)據(jù)流量的字節(jié)數(shù)
4 考慮的問(wèn)題
redis 集群?jiǎn)栴}
由于 redis 是集群環(huán)境, 集群環(huán)境下實(shí)際上直接執(zhí)行 lua 腳本是有問(wèn)題的。 試想 lua 腳本內(nèi)可能涉及到多個(gè) key 的操作, 而 redis 實(shí)際執(zhí)行節(jié)點(diǎn)的選擇也是通過(guò) key 來(lái)選擇的。 在多 key 情況下可能會(huì)造成 lua 腳本內(nèi) key 的執(zhí)行混亂, 所以我們需要先手動(dòng)選擇好 redis 節(jié)點(diǎn)。
此處我們可以先用限流的 key 將 redis 選擇出來(lái), 再將 lua 腳本傳到某個(gè) redis 節(jié)點(diǎn)執(zhí)行。 也就是我們必須要可以通過(guò)限流 key 唯一確定一個(gè) redis 節(jié)點(diǎn), 例如 url:/test:ip:192.168.1.1 是可以確定使用某個(gè) redis 節(jié)點(diǎn)的。
分布式時(shí)間問(wèn)題
分布式系統(tǒng)需要考慮多客戶端時(shí)間不一致問(wèn)題, 此處使用 redis 時(shí)間解決。
客戶端性能問(wèn)題
由于這是一個(gè)公用的限流服務(wù), 也就是所有接入該服務(wù)的應(yīng)用的每次請(qǐng)求都會(huì)調(diào)用該服務(wù), 再加上所有接入服務(wù)的應(yīng)用共用一個(gè) redis, 顯然如果客戶端使用同步等待限流服務(wù)的返回結(jié)果并不太合適, 會(huì)影響客戶端的服務(wù)調(diào)用性能。
所以我們可以使用一種折中策略, 即將限流結(jié)果保存到本地, 每次請(qǐng)求直接檢查本地限流結(jié)果是否被限流, 同時(shí)使用異步的方式調(diào)用限流服務(wù), 并在異步回調(diào)中更新限流結(jié)果。 這種做法會(huì)讓限流數(shù)據(jù)略有延遲, 但是影響不大。
限流服務(wù)本身的負(fù)載
作為限流服務(wù), 一個(gè)主要的作用是限制惡意流量對(duì)正常業(yè)務(wù)造成沖擊, 但如果所有流量都需要經(jīng)過(guò)限流服務(wù), 當(dāng)流量激增的時(shí)候, 誰(shuí)來(lái)保證限流服務(wù)自己不被壓垮? 我的建議是設(shè)定一個(gè)閾值, 當(dāng)流量超過(guò)某個(gè)閾值(一般來(lái)講, 這個(gè)閾值可以設(shè)置為 機(jī)器數(shù) * 限流閾值)時(shí), 直接退化為本地限流。
-
lua腳本
+關(guān)注
關(guān)注
0文章
21瀏覽量
7716 -
Redis
+關(guān)注
關(guān)注
0文章
387瀏覽量
11444
發(fā)布評(píng)論請(qǐng)先 登錄

分布式軟件系統(tǒng)
LED分布式恒流原理
分布式發(fā)電技術(shù)與微型電網(wǎng)
分布式聲波傳感系統(tǒng)DAS
如何設(shè)計(jì)分布式干擾系統(tǒng)?
分布式系統(tǒng)的優(yōu)勢(shì)是什么?
HarmonyOS應(yīng)用開(kāi)發(fā)-分布式設(shè)計(jì)
各種分布式電源的電氣特性
如何高效完成HarmonyOS分布式應(yīng)用測(cè)試?
分布式電源分布式電源裝置是指什么?有何特點(diǎn)
OpenHarmony3.1分布式技術(shù)資料合集
常見(jiàn)的分布式供電技術(shù)有哪些?
Redis實(shí)現(xiàn)分布式多規(guī)則限流的方式介紹

評(píng)論