限流的目的是通過對並發訪問/請求進行限速或者一個時間窗口內的請求進行限速來保護系統,將流量削峰防止系統掛掉或雪崩,最終做到有損服務而不是不服務。
4.1 限流算法
4.1.1 令牌桶算法
4.1.2 漏桶算法
4.1.3 計數器限流 比如用Redis的有序集合限流
4.2 應用級限流
4.2.1 限流總並發/連接/請求數
當快要超過系統QPS時,進行限流保護,新請求將被丟棄或者放到隊列中
4.2.2 限流總資源數
比如數據庫連接和線程,使用池化技術限制總資源數
4.2.3 限流某個接口的總並發數/請求數
單獨限制某個高頻接口,使用AtomicLong或者信號量,或者Hystrix的信號量模式
4.2.4 限流某個接口時間窗請求數
Guava的Cache統計一定時間窗內的請求次數;Redis的有序集合,score是時間,每次查詢時,將時間窗口之外的節點刪除掉,查詢一下總節點數
4.2.5 平滑限流某個接口的請求數
令牌桶和漏桶,以恆定或者相對恆定速率處理請求
4.3 分布式限流
分布式限流的關鍵是將限流做成原子化,是4.2的分布式場景下的延伸。針對的場景是業務層面的限流。
4.3.1 Redis+Lua
時間窗口實現:
鍵值對設計:比如限制每秒並發量,key取時間戳到秒級別,value存請求累加數。
判斷的流程:取key,不存在新設置,存在則判斷是否大於上限,沒超過則將累加數加一。過期時間設置1秒多一點。整個判斷寫在lua腳本中。
4.3.2 Nginx+Lua
4.4 接入層限流
流量入口處的限流,該層主要目的是:負載均衡、非法請求過濾、請求聚合、緩存、降級、限流、A/B測試、服務質量監控。
相對於分布式限流,這里的限制不涉及業務層面。
對於Nginx接入層限流可以使用自帶的兩個模塊,通過指定key,針對key進行限流:
連接數限流模塊:ngx_http_limit_conn_module。比如限定某IP下的總連接數
請求限流模塊:ngx_http_limit_req_module。漏桶算法實現,有平滑模式和允許突發模式,比如限定某IP下的總請求數
OpenRestry提供的Lua限流模塊:lua-retry-limit-traffic
4.5 節流
防止多個相同事件連續重復執行。
4.5.1 throttleFirst/throttleLast 在一個時間窗口內,多個重復事件只處理第一個或最后一個。
書中舉的例子,用戶滾動頁面觸發scroll事件,防止瀏覽器卡頓,多個滾動時間只會執行一次
4.5.2 throttleWithTimeout 兩個連續時間的先后執行時間不得小於某個時間窗口
書中舉的例子,關鍵詞自動補全,我們想要一個詞的補全而不是每個字的補全,如果每個字都調用補全接口,先輸入字的補全會很快被下一個字的補全覆蓋