一、前言
上一篇文章中粗淺的介紹使用Redis和基於令牌桶算法進行對服務接口API限流,本文介紹另一種算法---漏桶算法的應用。Nginx想必大家都有所了解是一個高性能的 HTTP 和反向代理服務器,優秀而強大的Nginx依然可以處理限制來自單個IP地址的請求處理頻率。ngx_http_limit_conn_module模塊可以限制請求數即通過定義的鍵值來限制請求處理的頻率。該模塊其采用漏桶算法,每秒固定處理請求數,推遲延遲請求。
二、ngx_http_limit_conn_module模塊指令
- limit_req_zone
語法: limit_req_zone $variable zone=name:size rate=rate;
默認值: none
配置段: http
說明:設置一塊共享內存限制域用來保存鍵值的狀態參數,尤其是保存了當前超出請求的數量。鍵的值就是指定的變量(空值不會被計算)。如:
limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;
區域名稱為one,大小為10m,平均處理的請求頻率不能超過每秒一次。
鍵值是客戶端IP。
使用$binary_remote_addr變量,可以將每條狀態記錄大小減小到64字節,十六萬多條記錄。
如果共享內存限制域的存儲空間耗盡了,對於后續的所有請求,nginx都會返回503錯誤。
速度可以設置為每秒處理請求數或每分鍾處理請求數,其值必須是整數。
- limit_req_log_level
語法: limit_req_log_level info | notice | warn | error;
默認值: limit_req_log_level error;
配置段: http, server, location
說明:設置你所希望的日志級別,當服務器因為頻率過高拒絕或者延遲處理請求時可以記下相應級別的日志。 延遲記錄的日志級別比拒絕的低一個級別;比如, 如果設置“limit_req_log_level notice”, 延遲的日志就是info級別。
- limit_req_status
語法: limit_req_status code
默認值: limit_req_status 503
配置段: http, server, location
說明:設置拒絕請求的響應狀態碼。
- limit_req
語法: limit_req zone=name [burst=number] [nodelay];
默認值: —
配置段: http, server, location
說明:設置對應的共享內存限制域和允許被處理的最大請求數閾值。 如果請求的頻率超過了限制域配置的值,請求處理會被延遲,所以所有的請求都是以定義的頻率被處理的。 超過頻率限制的請求會被延遲,直到被延遲的請求數超過了定義的閾值,這時,這個請求會被終止,並返回503 (Service Temporarily Unavailable) 錯誤。這個閾值的默認值為0。如:
limit_req_zone $binary_remote_addr zone=ttlsa_com:10m rate=5r/s; server { location /www.cnblogs.com/ { limit_req zone=cnblogs_com burst=5; } }
限制平均每秒不超過5個請求,同時允許超過頻率限制的請求數不多於5個。
如果不希望超過的請求被延遲,可以用nodelay參數,如:
limit_req zone=ttlsa_com burst=5 nodelay;
完整的配置
http { limit_req_zone $binary_remote_addr zone=cnblogs_com:10m rate=1r/s; server { location /www.cnblogs.com/ { limit_req zone=cnblogs_com burst=5; } } }
從上述的配置中,可以看出對所有的IP都進行了限制。在某些特殊情況下,我們不希望對某些IP限制如公司IP,那么白名單就有了用武之地,將不需要限制的IP加入到白名單中即可。白名單實現方法,需要結合geo和map指令來實現。看配置:
geo $whiteiplist { default 1; 127.0.0.1 0; 10.0.0.0/8 0; 107.155.42.0/24 0; } map $whiteiplist $limit { 1 $binary_remote_addr; 0 ""; } limit_conn_zone $limit zone=limit:10m; server { listen 8080; server_name www.cnblogs.com; location ^~ /cnblogs.com/ { limit_conn limit 4; limit_rate 200k; } } }
說明:
1. geo指令定義一個白名單$whiteiplist, 默認值為1, 所有都受限制。 如果客戶端IP與白名單列出的IP相匹配,則$whiteiplist值為0也就是不受限制。
2. map指令是將$whiteiplist值為1的,也就是受限制的IP,映射為客戶端IP。將$whiteiplist值為0的,也就是白名單IP,映射為空的字符串。
3. limit_conn_zone和limit_req_zone指令對於鍵為空值的將會被忽略,從而實現對於列出來的IP不做限制。
三、題外話
對於肆無忌憚的蜘蛛或者大流量惡意的攻擊訪問,會帶來帶寬的浪費和服務器巨大的壓力,影響業務的正常使用。
對於這些情況可以采取多種措施:
1、對同一個ip的連接數,並發數和請求書進行限制。
通過ngx_http_limit_conn_module和ngx_http_limit_conn_module模塊可以很好的達到效果。
上周玩客被百度蜘蛛給盯上了,百度蜘蛛對玩客的抓取頻率增加了5倍。百度蜘蛛抓取量驟增,導致服務器負載很高。最終用nginx的ngx_http_limit_req_module模塊限制了百度蜘蛛的抓取頻率。每分鍾允許百度蜘蛛抓取200次,多余的抓取請求返回503。
nginx的配置:
#全局配置
limit_req_zone $anti_spider zone=anti_spider:60m rate=200r/m;
#某個server中
limit_req zone=anti_spider burst=5 nodelay;
if ($http_user_agent ~* “baiduspider”) {
set $anti_spider $http_user_agent;
}參數說明:
指令linit_req_zone 中的rate=200r/m 表示每分鍾只能處理200個請求。
指令limit_req 中的burst=5 表示最大並發為5。即同一時間只能同時處理5個請求。
指令limit_req 中的 nodelay 表示當已經達到burst值時,再來新請求時,直接返回503
IF部分用於判斷是否是百度蜘蛛的user agent。如果是,就對變量$anti_spider賦值。這樣就做到了只對百度蜘蛛進行限制了。
2、對惡意的IP訪問攻擊
首先要將惡意的IP擋在緩存之外,減小穿透緩存把壓力打到DB上。
其次對於的惡意的IP進行封IP甚至封IP段。
ngx_http_access_module模塊中的deny和allow指令
allow
語法: allow address | CIDR | unix: | all;
默認值: —
配置段: http, server, location, limit_except
允許某個ip或者一個ip段訪問.如果指定unix:,那將允許socket的訪問.注意:unix在1.5.1中新加入的功能,如果你的版本比這個低,請不要使用這個方法。
deny
語法: deny address | CIDR | unix: | all;
默認值: —
配置段:http, server, location, limit_except
禁止某個ip或者一個ip段訪問.如果指定unix:,那將禁止socket的訪問.注意:unix在1.5.1中新加入的功能,如果你的版本比這個低,請不要使用這個方法。
location / {
deny 107.155.42.59;
allow 107.155.42.60/66;
deny all;
}
從上到下的順序,匹配到了便跳出。如上的例子先禁止了107.155.42.59,接下來允許了1個網段,最后未匹配的IP全部禁止訪問。在實際生產環境中,我們也會使用nginx 的geo模塊配合使用。
deny和allow是nginx里面最基本的指令,如果想禁止誰訪問就添加指令deny加上IP,想允許則加上指令allow ip;如果想禁止或者允許所有,那么allow all或者deny all即可。
參考:
http://www.bo56.com/%E4%BD%BF%E7%94%A8nginx%E9%99%90%E5%88%B6%E7%99%BE%E5%BA%A6%E8%9C%98%E8%9B%9B%E7%9A%84%E9%A2%91%E7%B9%81%E6%8A%93%E5%8F%96/
由於本人經驗有限,文章中難免會有錯誤,請瀏覽文章的您指正或有不同的觀點共同探討!