關於限流

我們的web服務可能由於過高的qps導致系統崩潰,為了應對這一現象,衍生出了負載均衡,服務降級,快取等技術,這裡簡單總結一下另一種保護系統的技術--限流。
限流,顧名思義,就是限制一段時間內的流量,使流量保持在可控範圍之內,保障系統安全。限流有應用層面與軟體層面之分。
軟體限流-nginx
nginx作為廣泛使用的負載均衡器,功能也是十分齊全,nginx提供了2個限流相關的模組
ofollow,noindex">ngx_http_limit_conn_module :限制單位時間內的連線數,單一IP有效。
ngx_http_limit_req_module :限制同一時間q請求,對單一IP有效。
具體的如何配置可以點選連線檢視官方文件。
應用層面
nginx的限流是第一道關卡,但只能對單一IP生效,所以在程式層面我們也要做限流。程式層面限流大致有以下幾種演算法。
1.計數器法
這個演算法比較簡單,設定一個計數器,計算一段時間內通過請求的數量,超過預定值則直接拒絕。這裡的計數器需要使用java併發包中的cas類。演算法缺點比較明顯,比如我們限制一分鐘內只能通過100次請求,那麼前5秒內就已經請求100次了,那麼後55秒內的請求只能拒絕,導致請求分配不均勻。
2.漏桶演算法

演算法原理如圖,桶的容量是固定的,並且出水速度是固定的。假如限制一秒內只能通過10個請求,那麼不管請求流量多大,處理速度是恆定的。但是如果桶容量很快就滿了的話,新進來的請求就要被丟棄,假如短時間內進入大量請求導致桶滿,那麼後來一段時間內的請求只能拒絕,缺點和計數器演算法比較類似。
3.令牌桶演算法
這個演算法是大家廣泛採用的限流演算法,很大程度上解決了上面倆種演算法的缺點,原理是用一個桶存放固定數量的令牌,以固定的速度往桶裡放令牌,每次呼叫需要先獲取令牌。當桶中令牌達到上限,就丟棄新生成的令牌。該演算法可以使請求速度分散並且允許一定程度的大量請求呼叫。

該演算法已經有現成的類庫做了封裝--大名鼎鼎的guava。RateLimiter即為限流器類,下面是幾個常用的方法,具體使用詳見官方文件。
RateLimiter r = RateLimiter.create(10); //建立一個qps最大為10的限流器
r.acquire() // 表示阻塞直到獲取一個令牌
r.tryAcquire() //如果沒有可用令牌,就直接返回false,該方法可以指定超時時間。
最後
上面說的方法都是單機限流的實現,並不能應用到叢集,關於叢集限流一般需藉助第三方工具實現,比如redis。