nginx之限流詳解(請(qǐng)求/并發(fā)量)
簡(jiǎn)述
在做日常的web運(yùn)維工作中,難免會(huì)遇到服務(wù)器流量異常,負(fù)載過大等情況。
惡意攻擊訪問/爬蟲等非正常性請(qǐng)求,會(huì)帶來帶寬的浪費(fèi),服務(wù)器壓力增大,影響業(yè)務(wù)質(zhì)量。
限流方案
對(duì)于這種情況, 一般可考慮通過nginx的ngx_http_limit_conn_module模塊和ngx_http_limit_req_module 模塊限制同一client ip的請(qǐng)求量和并發(fā)數(shù)來防范。
1. ngx_http_limit_conn_module模塊(限制連接數(shù)[并發(fā)])
limit_conn_zone
- 語法: limit_conn_zone $variable zone=name:size;
- 默認(rèn)值: none
- 配置段: http
$variable定義鍵(鍵的狀態(tài)中保存了當(dāng)前連接數(shù)),zone=name定義區(qū)域名稱,后面的limit_conn指令會(huì)用到的。
size定義各個(gè)鍵共享內(nèi)存空間大小(當(dāng)共享內(nèi)存空間被耗盡,服務(wù)器將會(huì)對(duì)后續(xù)所有的請(qǐng)求返回 503 錯(cuò)誤。)。
limit_conn_log_level
- 語法:limit_conn_log_level info | notice | warn | error
- 默認(rèn)值:error
- 配置段:http, server, location
- 當(dāng)達(dá)到最大限制連接數(shù)后,記錄日志的等級(jí)。
limit_conn
- 語法:limit_conn zone_name number
- 默認(rèn)值:none
- 配置段:http, server, location
- 指定每個(gè)給定鍵值的最大同時(shí)連接數(shù),當(dāng)超過這個(gè)數(shù)字時(shí)被返回503
limit_conn_status
- 語法: limit_conn_status code;
- 默認(rèn)值: limit_conn_status 503;
- 配置段: http, server, location
- 該指定在1.3.15版本引入的。指定當(dāng)超過限制時(shí),返回的狀態(tài)碼。默認(rèn)是503。
limit_rate
- 語法:limit_rate rate
- 默認(rèn)值:0
- 配置段:http, server, location, if in location
對(duì)每個(gè)連接的速率限制。參數(shù)rate的單位是字節(jié)/秒,設(shè)置為0將關(guān)閉限速。
按連接限速而不是按IP限制,因此如果某個(gè)客戶端同時(shí)開啟了兩個(gè)連接,那么客戶端的整體速率是這條指令設(shè)置值的2倍。
2. ngx_http_limit_req_module模塊(限制請(qǐng)求數(shù))
limit_req_zone
- 語法: limit_req_zone $variable zone=name:size rate=rate;
- 默認(rèn)值: none
- 配置段: http
設(shè)置一塊共享內(nèi)存限制域用來保存鍵值的狀態(tài)參數(shù),速度可以設(shè)置為每秒處理請(qǐng)求數(shù)和每分鐘處理請(qǐng)求數(shù),其值必須是整數(shù),所以如果你需要指定每秒處理少于1個(gè)的請(qǐng)求,2秒處理一個(gè)請(qǐng)求,可以使用 “30r/m”
limit_req_log_level
- 語法: limit_req_log_level info | notice | warn | error;
- 默認(rèn)值: limit_req_log_level error;
- 配置段: http, server, location
設(shè)置你所希望的日志級(jí)別
limit_req_status
- 語法: limit_req_status code;
- 默認(rèn)值: limit_req_status 503;
- 配置段: http, server, location
limit_req
- 語法: limit_req zone=name [burst=number] [nodelay];
- 默認(rèn)值: —
- 配置段: http, server, location
設(shè)置對(duì)應(yīng)的共享內(nèi)存限制域和允許被處理的最大請(qǐng)求數(shù)閾值。 如果請(qǐng)求的頻率超過了限制域配置的值,請(qǐng)求處理會(huì)被延遲,當(dāng)被延遲的請(qǐng)求數(shù)超過了定義的閾值,這個(gè)請(qǐng)求會(huì)被終止,并返回503;nodelay:表示當(dāng)超過訪問次數(shù)并緩沖也滿的情況下,直接放回503錯(cuò)誤,若不設(shè)置,這些多余的請(qǐng)求會(huì)延遲處理 。
實(shí)際運(yùn)用案例
一般情況下key使用$binary_remote_addr(建議使用)或者$remote_addr變量二者區(qū)別在于: $remote_addr變量的長(zhǎng)度為7字節(jié)到15字節(jié),而存儲(chǔ)狀態(tài)在32位平臺(tái)中占用32字節(jié)或64字節(jié),在64位平臺(tái)中占用64字節(jié)。
$binary_remote_addr變量的長(zhǎng)度是固定的4字節(jié),存儲(chǔ)狀態(tài)在32位平臺(tái)中占用32字節(jié)或64字節(jié),在64位平臺(tái)中占用64字節(jié)。
1M共享空間可以保存3.2萬個(gè)32位的狀態(tài),1.6萬個(gè)64位的狀態(tài)。當(dāng)恭喜內(nèi)存空間被耗盡時(shí),服務(wù)會(huì)對(duì)后續(xù)請(qǐng)求返回503(切記).
http{
.........
limit_conn_zone $binary_remote_addr zone=addr:1000m;
limit_req_zone $binary_remote_addr zone=addrs:1000m rate=10r/s; #每秒請(qǐng)求10次
...........
server{
......
limit_conn addr 10; #限制并發(fā)數(shù)為10
limit_req zone=addrs burst=5 nodelay; #允許超出請(qǐng)求5(排隊(duì)/延遲)
......
}
}
#注 limit_conn_zone 和limit_req_zone可單獨(dú)使用除以上以$binary_remote_addr變量為key外,還有一種情況,如前端使用了代理層,CDN等服務(wù),這種情況下$binary_remote_addr變量會(huì)是代理層/CDN的ip,就無法通過$binary_remote_addr變量做限制, 目前知道有兩種解決方式,一種是使用白名單, 另外一種是配置$http_x_forwarded_for然后取逗號(hào)分隔的第一個(gè)ip(即源ip,不太好的消息就是這個(gè)變量字段是可以通過修改請(qǐng)求head信息進(jìn)行偽造(curl -H "X-Forwarded-For:192.168.0.1,192.168.0.2" http://127.0.0.1 ))。
白名單的方式后續(xù)再說。
這里先說下第二種方法, 可通過nginx的map模塊做正則匹配后映射請(qǐng)求源ip, 具體配置如下:
1)代理層的配置:
upstream renren_trans_1 {
server 10.5.11.12;
} server {
listen 80;
server_name zabbixtest.d.xiaonei.com;
http_accounting_id zabbixtest;
location / {
proxy_pass http://renren_trans_1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 50m;
client_body_buffer_size 128k;
client_body_temp_path /data/client_body_temp;
proxy_connect_timeout 15;
proxy_send_timeout 300;
proxy_read_timeout 300;
proxy_buffer_size 8k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
proxy_temp_path /data/proxy_temp;
}
}代理層后端(業(yè)務(wù)機(jī))配置:
map $http_x_forwarded_for $clientips {
"" $remote_addr; #當(dāng)為空時(shí)映射$remote_addr 到$clientips
~^(?P<firstip>[0-9\.]+),?.*$ $firstip; #獲取第一個(gè)ip
}
limit_conn_zone $clientips zone=addr:1000m;
limit_req_zone $clientips zone=addrs:1000m rate=10r/s;
limit_req zone=addrs burst=5 nodelay;
server {
listen 80;
server_name localhost;
limit_conn addr 10;
limit_req zone=addrs burst=5 nodelay;
#charset koi8-r;
#access_log logs/host.access.log main;測(cè)試:
1) 測(cè)試通過代理層訪問的方式:
# ab -c 11 -t 10 http://zabbixtest.d.xiaonei.com/test.html
access.log內(nèi)容如下:
可發(fā)現(xiàn)大量503 error(第一列代理層,最后一列源ip) 。
2)測(cè)試直接訪問,不通過代理層
# ab -c 5 -t 10 http://10.5.11.12/test.html
access.log如下:
同樣會(huì)出現(xiàn)大量503 ,代理ip為空!
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Nginx利用Lua+Redis實(shí)現(xiàn)動(dòng)態(tài)封禁IP的方法
在站點(diǎn)遇到攻擊且無明顯攻擊特征,造成站點(diǎn)訪問慢,nginx不斷返回502等錯(cuò)誤時(shí),可利用nginx+lua+redis對(duì)該IP進(jìn)行封禁,這篇文章主要給大家介紹了關(guān)于Nginx利用Lua+Redis實(shí)現(xiàn)動(dòng)態(tài)封禁IP的相關(guān)資料,需要的朋友可以參考下2018-12-12
nginx proxy_buffer_size解決后端服務(wù)傳輸數(shù)據(jù)過多,header過大問題
這篇文章主要介紹了nginx proxy_buffer_size解決后端服務(wù)傳輸數(shù)據(jù)過多,header過大問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
nginx調(diào)用php-fpm出錯(cuò)解決方法和nginx配置詳解
這篇文章介紹了nginx調(diào)用php-fpm出錯(cuò)的解決方法,最后給出了nginx配置方法,需要的朋友可以參考下2014-03-03
詳解nginx+php執(zhí)行請(qǐng)求的工作原理
這篇文章主要介紹了詳解nginx+php執(zhí)行請(qǐng)求的工作原理,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-02-02
Nginx服務(wù)器抵御CC攻擊的相關(guān)配置講解
這篇文章主要介紹了Nginx服務(wù)器抵御CC攻擊的相關(guān)配置講解,CC攻擊原理與DDoS基本相似而且技術(shù)含量低,注意防范即可,需要的朋友可以參考下2016-01-01

