在開發高並發系統時有三把利器用來保護系統:緩存、降級和限流。本文結合作者的一些經驗介紹限流的相關概念、算法和常規的實現方式。
緩存
緩存比較好理解,在大型高並發系統中,如果沒有緩存數據庫將分分鍾被爆,系統也會瞬間癱瘓。使用緩存不單單能夠提升系統訪問速度、提高並發訪問量,也是保護數據庫、保護系統的有效方式。大型網站一般主要是“讀”,緩存的使用很容易被想到。
在大型“寫”系統中,緩存也常常扮演者非常重要的角色。比如累積一些數據批量寫入,內存里面的緩存隊列(生產消費),以及HBase寫數據的機制等等也都是通過緩存提升系統的吞吐量或者實現系統的保護措施。甚至消息中間件,你也可以認為是一種分布式的數據緩存。
降級
服務降級是當服務器壓力劇增的情況下,根據當前業務情況及流量對一些服務和頁面有策略的降級,以此釋放服務器資源以保證核心任務的正常運行。降級往往會指定不同的級別,面臨不同的異常等級執行不同的處理。根據服務方式:可以拒接服務,可以延遲服務,也有時候可以隨機服務。
根據服務范圍:可以砍掉某個功能,也可以砍掉某些模塊。總之服務降級需要根據不同的業務需求采用不同的降級策略。主要的目的就是服務雖然有損但是總比沒有好。
限流
限流可以認為服務降級的一種,限流就是限制系統的輸入和輸出流量已達到保護系統的目的。一般來說系統的吞吐量是可以被測算的,為了保證系統的穩定運行,一旦達到的需要限制的閾值,就需要限制流量並采取一些措施以完成限制流量的目的。
比如:延遲處理,拒絕處理,或者部分拒絕處理等等。
限流方式之一:
Nginx
對於Nginx接入層限流可以使用Nginx自帶了兩個模塊:
- 連接數限流模塊ngx_http_limit_conn_module
- 漏桶算法實現的請求限流模塊ngx_http_limit_req_module
1. ngx_http_limit_conn_module
我們經常會遇到這種情況,服務器流量異常,負載過大等等。對於大流量惡意的攻擊訪問,會帶來帶寬的浪費,服務器壓力,影響業務,往往考慮對同一個ip的連接數,並發數進行限制。
ngx_http_limit_conn_module 模塊來實現該需求。該模塊可以根據定義的鍵來限制每個鍵值的連接數,如同一個IP來源的連接數。並不是所有的連接都會被該模塊計數,只有那些正在被處理的請求(這些請求的頭信息已被完全讀入)所在的連接才會被計數。
我們可以在nginx_conf的http{}中加上如下配置實現限制:
#限制每個用戶的並發連接數,取名one
limit_conn_zone $binary_remote_addr zone=one:10m; #配置記錄被限流后的日志級別,默認error級別 limit_conn_log_level error; #配置被限流后返回的狀態碼,默認返回503 limit_conn_status 503;
然后在server{}里加上如下代碼:
#限制用戶並發連接數為1
limit_conn one 1;
然后我們是使用ab測試來模擬並發請求:
ab -n 5 -c 5 http://10.23.22.239/index.html
得到下面的結果,很明顯並發被限制住了,超過閾值的都顯示503:

另外剛才是配置針對單個IP的並發限制,還是可以針對域名進行並發限制,配置和客戶端IP類似。
#http{}段配置 limit_conn_zone $ server_name zone=perserver:10m; #server{}段配置 limit_conn perserver 1;
2. ngx_http_limit_req_module
上面我們使用到了ngx_http_limit_conn_module 模塊,來限制連接數。那么請求數的限制該怎么做呢?這就需要通過ngx_http_limit_req_module 模塊來實現,該模塊可以通過定義的鍵值來限制請求處理的頻率。
特別的,可以限制來自單個IP地址的請求處理頻率。限制的方法是使用了漏斗算法,每秒固定處理請求數,推遲過多請求。如果請求的頻率超過了限制域配置的值,請求處理會被延遲或被丟棄,所以所有的請求都是以定義的頻率被處理的。
在http{}中配置
#區域名稱為one,大小為10m,平均處理的請求頻率不能超過每秒一次。
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
在server{}中配置
#設置每個IP桶的數量為5
limit_req zone=one burst=5;
上面設置定義了每個IP的請求處理只能限制在每秒1個。並且服務端可以為每個IP緩存5個請求,如果操作了5個請求,請求就會被丟棄。
使用ab測試模擬客戶端連續訪問10次:
ab -n 10 -c 10 http://10.23.22.239/index.html
如下圖,設置了通的個數為5個。一共10個請求,第一個請求馬上被處理。第2-6個被存放在桶中。由於桶滿了,沒有設置nodelay因此,余下的4個請求被丟棄。
