Nginx下limit_req模塊burst參數超詳細解析

在學習Nginx的時候遇到了這個問題,百度到了很多博客,大多都講得不清不楚,在看到了

http://www.wangjingfeng.com/730.html

http://cjhust.blog.163.com/blog/static/17582715720111017114121678/

這兩篇博客之後終於疑竇瞬開,在綜合了之前看到的博客再加上測試案例之後整理成文。

引言:

漏斗算法(Leaky Bucket),該算法有兩種處理方式Traffic Shaping和Traffic Policing

在桶滿水之後,常見的兩種處理方式爲:

  1. 暫時攔截住上方水的向下流動,等待桶中的一部分水漏走後,再放行上方水。
  2. 溢出的上方水直接拋棄。

 

將水看作網絡通信中數據包的抽象,則方式1起到的效果稱爲Traffic Shaping,方式2起到的效果稱爲Traffic Policing

由此可見,Traffic Shaping的核心理念是"等待",Traffic Policing的核心理念是"丟棄"。它們是兩種常見的流速控制方法

 

源碼:

limit_req模塊的源碼在src/http/modules/ngx\_http\_limit\_req\_module.c,關於burst的核心代碼在這。//src/http/modules/ngx_http_limit_req_module.c:396

ms = (ngx_msec_int_t) (now - lr->last);
excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
if (excess < 0) {
    excess = 0;
}
*ep = excess;
if ((ngx_uint_t) excess > limit->burst) {
   return NGX_BUSY;
}
if (account) {
    lr->excess = excess;
    lr->last = now;
   return NGX_OK;
}

excess初始值是0,假設現在ctx->rate是2000(即2 request/s),這次請求距離上次請求是400ms。

那麼excess = 0 – 2000 * 400 / 1000 + 1000 = 200。如果limit->burst是0,那麼200 > 0,會返回NGX_BUSY即是503了。

假如burst是1,limit->burst即是1000,那麼如果請求是每隔400ms來一個,共需5個纔會填滿limit->burst(每個請求將會增加200 excess),到第6個纔會返回503。

推導出公式,假設設置頻率是r request/s,每次請求距離上次請求t ms,設置burst爲b,

那麼返回503的臨界請求個數x是

\begin{equation}

x = floor(b * \frac {( 1000 / r )} {( 1000 / r – t )})

\end{equation}

雖然知道了源碼但是還是沒有一個非常直觀的感覺,所以不如實際操作一下:

 

實際操作:

nginx中該模塊的使用配置示例l

imit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

server {

    location  /search/ {

        limit_req zone=one burst=5 nodelay;

    }

第一段配置參數:

  • $binary_remote_addr :表示通過remote_addr這個標識來做限制,“binary_”的目的是縮寫內存佔用量,是限制同一客戶端ip地址
  • zone=one:10m:表示生成一個大小爲10M,名字爲one的內存區域,用來存儲訪問的頻次信息
  • rate=1r/s:表示允許相同標識的客戶端的訪問頻次,這裏限制的是每秒1次,即每秒只處理一個請求,還可以有比如30r/m的,即限制每2秒訪問一次,即每2秒才處理一個請求

 

第二段配置參數:

  • zone=one :設置使用哪個配置區域來做限制,與上面limit_req_zone 裏的name對應
  • burst=5:重點說明一下這個配置,burst爆發的意思,這個配置的意思是設置一個大小爲5的緩衝區當有大量請求(爆發)過來時,超過了訪問頻次限制的請求可以先放到這個緩衝區內等待,但是這個等待區裏的位置只有5個,超過的請求會直接報503的錯誤然後返回。
  • nodelay:
    • 如果設置,會在瞬時提供處理(burst + rate)個請求的能力,請求超過(burst + rate)的時候就會直接返回503,永遠不存在請求需要等待的情況。(這裏的rate的單位是:r/s)
    • 如果沒有設置,則所有請求會依次等待排隊

 

     這裏的burst參數主要採用了令牌桶算法。令牌桶算法是網絡流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常用的一種算法。典型情況下,令牌桶算法用來控制發送到網絡上的數據數目,並允許突發數據的發送。

     令牌桶這種控制機制基於令牌桶中是否存在令牌來指示什麼時候可以發送流量。令牌桶中的每一個令牌都代表一個字節。如果令牌桶中存在令牌,則允許發送流量;如果令牌桶中不存在令牌,則不允許發送流量。因此,如果突發門限被合理地配置並且令牌桶中有足夠的令牌,那麼流量就可以以峯值速率發送。令牌桶算法的基本過程如下:

 

  1. 假如用戶配置的平均發送速率爲10r/s,則每隔0.1秒一個令牌被加入到桶中;
     
  2. 假設桶最多可以存發b個令牌。如果令牌到達時令牌桶已經滿了,那麼這個令牌會被丟棄;
     
  3. 當一個n個字節的數據包到達時,就從令牌桶中刪除n個令牌,並且數據包被髮送到網絡;
     
  4. 如果令牌桶中少於n個令牌,那麼不會刪除令牌,並且認爲這個數據包在流量限制之外;
     
  5. 算法允許最長b個字節的突發,但從長期運行結果看,數據包的速率被限制成常量r。對於在流量限制外的數據包可以以不同的方式處理:
    • 它們可以被丟棄;
    • 它們可以排放在隊列中以便當令牌桶中累積了足夠多的令牌時再傳輸;
    • 它們可以繼續發送,但需要做特殊標記,網絡過載的時候將這些特殊標記的包丟棄。

 

注意:令牌桶算法不能與另外一種常見算法“漏斗算法(Leaky Bucket)”相混淆。這兩種算法的主要區別在於“漏斗算法”能夠強行限制數據的傳輸速率,而“令牌桶算法”在能夠限制數據的平均傳輸數據外,還允許某種程度的突發傳輸。在“令牌桶算法”中,只要令牌桶中存在令牌,那麼就允許突發地傳輸數據直到達到用戶配置的門限,因此它適合於具有突發特性的流量。(這部分是網上很多博客都錯誤的地方)

 

例子演示:

首先我們配置了limt_req_zone,rate=10r/m,即每六秒才處理一次請求,如下:

1.首先測試不加burst不加nodelay的情況:

  • 查看當前的tcp連接數
    • netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
    • 結果如下:

                   

  • 使用ab測試工具,發起10個併發請求:
    • ab -n 10 -c 10 url
    • ab壓測工具瞬間返回了結果
    • 可以看到一共10個請求,9個請求都失敗了。且0.09秒就完成了壓測
  • 接着查看當前的tcp連接數: 可以觀察到此時服務端的TIME_WAIT 爲10,這意味着是服務端主動要求斷開了所有TCP連接
  • 接着再查看 /var/log/nginx/access.log,印證了只有一個請求成功了,其它就是都直接返回了503,即服務器拒絕了請求。

 

2.只加burst不加nodelay的情況:

  • 查看當前的tcp連接數
    • netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
    • 結果如下:

                   

  • 使用ab測試工具,發起10個併發請求:
    • ab -n 10 -c 10 url
    • 可以看到測試經過30 s 才結束
    • 壓測中一共10個請求,有4個請求失敗了,直接返回了503
  • 查看當前的tcp連接數
              
    • 上圖是ab測試第一秒時的截圖,TIME_WAIT=5 表示有服務器端主動斷開了5個TCP連接,即5個請求被瞬時拒絕,同時ESTABLISHED的數量由23增加到28,即建立了5個TCP連接
    • 上圖是ab測試過程中的截圖,TIME_WAIT=7 表示有服務器端主動斷開了7個TCP連接,增加的2個TIME_WAIT是因爲有2個在緩存隊列的請求被處理完畢了,所以斷開了連接。
       
  • 接着查看 /var/log/nginx/access.log日誌
    • 可以觀察到在39分35秒,即壓測第1秒時,成功處理了1個請求,另外有4個請求瞬間返回了503,剩下的5個請求每隔6s處理一次。
    • 這是因爲設置了burst=5,在服務器接收到10個併發請求後,先處理1個請求,同時將5個請求放入burst緩衝隊列中,等待處理。而超過(burst+1)數量的請求就被直接拋棄了,即直接拋棄了4個請求。
  • 查看 /var/log/nginx/error.log日誌
    • 發現有5個delaying request,4個limiting request

 

3.加burst加nodelay的情況:

  • 查看當前的tcp連接數
    • netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
    • 結果如下:

                   

  • 使用ab測試工具,發起10個併發請求:
    • ab -n 10 -c 10 url
    • 可以看到壓測在0.1 s 內完成了,這也是添加nodelay參數的意義
    • 壓測中一共10個請求,有4個請求失敗了,直接返回了503
  • 查看當前的tcp連接數:所有的請求都在1s內處理完成了
  • 接着查看 /var/log/nginx/access.log日誌
    • 可以發現在1s內,服務器端處理了6個請求(峯值速度:burst+原來的處理速度)。對於剩下的4個請求,直接返回503,在下一秒如果繼續向服務端發送10個請求,服務端會直接拒絕這10個請求並返回503。因爲設定了沒6s處理1個請求,所以直到30 s 之後,纔可以再處理一個請求,即如果此時向服務端發送10個請求,會返回9個503,一個200
  • 查看/var/log/nginx/error.log日誌,發現有4個請求被直接拒絕了,沒有延時請求。

 

 

總結:

  • limit_req zone=req_zone;
    • 嚴格依照在limti_req_zone中配置的rate來處理請求
    • 超過rate處理能力範圍的,直接drop
    • 表現爲對收到的請求無延時
  • limit_req zone=req_zone burst=5;
    • 依照在limti_req_zone中配置的rate來處理請求
    • 同時設置了一個大小爲5的緩衝隊列,在緩衝隊列中的請求會等待慢慢處理
    • 超過了burst緩衝隊列長度和rate處理能力的請求被直接丟棄
    • 表現爲對收到的請求有延時
  • limit_req zone=req_zone burst=5 nodelay;
    • 依照在limti_req_zone中配置的rate來處理請求
    • 同時設置了一個大小爲5的緩衝隊列,當請求到來時,會爆發出一個峯值處理能力,對於峯值處理數量之外的請求,直接丟棄
    • 在完成峯值請求之後,緩衝隊列不能再放入請求。如果rate=10r/m,且這段時間內沒有請求再到來,則每6 s 緩衝隊列就能回覆一個緩衝請求的能力,直到回覆到能緩衝5個請求位置。

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章