限流
限流顧名思義就是限制流量,在軟件系統中就是限制流量進入軟件系統。
為什么要限流?
在實際的生活場景中,當一個 web 服務部署到生產環境,也就是我們所說的公網。這個時候就會受到互聯網上所有人的訪問請求,比如像百度。每天都會有很多人訪問 www.baidu.com ,如果有些人不懷好意的拼命的訪問這個網站,那么整個系統就會因為這個人的惡作劇,從而浪費了很多不必要的帶寬和系統資源。
限流實現
因為我們現在的軟件系統都是微服務形式的,一個 HTTP 請求可能要經過后端十幾個軟件服務,最后才能得到結果返回給用戶。如果我們對一些請求進行限制,比如只允許某一個 IP 在 10 秒鍾內訪問 20 次,如果超出了這 20 次,直接最前端就返回 429 狀態碼。這樣就保護了后端十幾個服務,避免為這些惡意請求消耗系統資源。
常見的幾種限流算法。
有想法就會有實現,當前最常見的幾種限流算法有
固定窗口計數器算法 、滑動窗口計數器算法、漏桶算法、令牌桶算法。
其中固定窗口計數器算法和滑動窗口計數器算法比較相似,漏桶算法和令牌桶算法比較類似。
以下我們用只允許一個 ip 在 10 秒鍾內只能訪問 20 次這個限流需求來解釋這些限流算法
固定窗口算法
固定窗口算法就是設置一個固定的時間期限,當第一條請求到來的時候就開始計時同時計數,當接下來的 10 秒中內每來一條請求計數器就+1。當計數器值到 20 后,接下來的所有請求都拒絕。十秒鍾過后重置計數器。
但是此算法存在一個缺陷:
假設以下一種場景,攻擊者在知道限流窗口是 10s 的情況下,先發送一條消息,讓限流算法開啟計數,此時計數器為 1,然后等到第 9.5 秒的時候持續發送請求攻擊,這樣 9.5-10 這個時間窗口里面會被允許經過 19 條消息,過了 10 秒后計數器歸零,又馬上接收了 20 條請求,這樣,在 9.5-10.5 這個時間窗口總共接收了 39 條消息,限流值直接放大了一倍(原本是希望最大一秒鍾只有 10 條的並發量)

滑動窗口算法
滑動窗口算法就解決了上面固定窗口算法的缺陷。所謂的滑動窗口就是在原有的固定窗口上新增了一個和固定窗口大小一樣的窗口,此窗口可以滑動如下圖。

當第1條消息來到的時候,10秒的窗口期就生成,此時滑動窗口和第1個窗口重疊。接下來在第9.5秒的時候開始發動請求攻擊,在第10秒的那一刻,滑動窗口。所含鈣的窗口里面的計數器的值已經達到20,接下來我們假設又過了0.5秒,此時滑動窗口來到10.5秒。這時候滑動窗口涵蓋了兩個窗口。此時如何計算滑動窗口中計數器的值呢?我們可以假設前面的窗口所過來的流量是按照時間均勻分布的(雖然實際上並不是)。那么這個時候我們就可以計算出一個權重。就是滑動窗口涵蓋第1個窗口時間的百分比:9.5/10=0.95。那么我們就假設當前這個窗口中所占有的數據為20*0.95=19。因此接下來我們只能允許通過一條數據。同時我們也可以計算出,10-10.5秒這個區間內算出來的值肯定小於1,因此這個區間內過來的請求全部會拒絕。
漏桶算法
漏桶算法的思想類似於小時候的那道數學題,一個水缸一個水龍頭放水,一個出水口出水,進水口就是攻擊者的請求,放水口就是限流算法允許通過的請求的速率,當水缸滿出來了,則將請求拒絕,水缸里的水就是攻擊者的請求被緩存起來。

接下來還是拿上面的例子:假設水缸容量是20,放水速率是每秒2個,當攻擊者突然一秒鍾打過來30個請求,如果是窗口計數器算法(不管是固定還是滑動窗口)會直接一下子允許20個請求通過剩下10個拒絕,但是接下來剩余窗口時間內不允許有新的請求進來。而漏桶算法則是會緩存這20個請求剩下10個拒絕,然后以每秒2個請求的速率往下游傳遞。此算法不允許突發流量,永遠保證下游的速率一致。
令牌桶算法
令牌桶算法則是在漏桶算法上進行了修改。它的思想是假設桶內有很多令牌,同時以固定速率生成令牌放到桶內,如果桶內令牌滿則丟棄。當請求過來的時候只要能拿到令牌就能通行。
以上面例子為例:桶內存在20個令牌,當同時以每秒2個的速度生成令牌。當一次來30條請求,則由於桶內存在20個令牌,因此前20個請求都會被放行,剩下的10個請求都會被拒絕,接下來如果繼續有請求過來的話,就會以每秒2個請求的速率放行,當一段時間沒有請求后,桶內令牌又會存滿。

總結
總共介紹了四種常見的算到,
- 固定窗口算法實現簡單,但是有缺點就是會超出限流閾值兩倍的請求
- 滑動窗口可以解決固定窗口超出限流閾值的問題,到時他的計算權重並不是准確的,而是按照時間線將請求平均在時間線上
- 漏桶算法不允許一定的突發流量,這有時候可能在8特定場景造成請求超時。
- 令牌桶允許突發流量
