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