1. 程式人生 > >Nginx訪問限制模組ngx_http_limit_req_module與ngx_http_limit_conn_module(限制高併發防止DDOS攻擊)

Nginx訪問限制模組ngx_http_limit_req_module與ngx_http_limit_conn_module(限制高併發防止DDOS攻擊)

Nginx訪問限制 (限制高併發防止DDOS攻擊)

ngx_http_limit_conn_module 連線頻率限制 ngx_http_limit_req_module 請求頻率限制

http協議的連線與請求

HTTP是建立在TCP基礎上,在完成HTTP請求需要先建立TCP三次握手(稱為TCP連線),在連線的基礎上在HTTP請求。

HTTP請求建立在一次TCP連線基礎上 一次TCP請求至少產生一次HTTP請求(一個連線可以有多個請求,建立一次三次握手可以傳送多個請求)

HTTP協議 連線關係
HTTP1.0 TCP不能複用
HTTP1.1 順序性TCP複用
HTTP2.0 多路複用TCP複用

Nginx請求限制配置:

# 必須在全域性定義請求限制
Syntax: limit_conn_zone key zone=name:size rate=rate;   #zone的名稱大小及速率
Default: -
Context: http

# 引用請求限制
Syntax: limit_conn zone number [burst=number] [nodelay];
Default: -
Context: http,server,location

# http模組配置
limit_req_zone $binary_remote_addr zone=req_zone:1m rate=1r/s;
    > $binary_remote_addr比$remote_addr更節省位元組,是限制同一客戶端ip地址
    > zone=req_zone:1m表示名稱是req_zone的記憶體區域,用來儲存訪問的頻次資訊,1m用來儲存session,1m能儲存16000個狀態
    > rate限制速率為1秒最多1個IP請求,rete的值必須為整數
    
# location模組引用配置
limit_req zone=req_zone;

# location模組引用req_zone配置,請求超過1r/s,剩下的將被延遲處理,burst=3緩衝區大小3,nodelay瞬時提供處理(burst + rate)個請求的能力,多餘的直接返回503
limit_req zone=req_zone burst=3 nodelay;

http {
...
limit_req_zone $binary_remote_addr zone=req_zone:1m rate=1r/s;
server {
        listen  80;
        server_name localhost;
        root    /usr/share/nginx/html/www;
        index   index.html;

        location / {
                root    /usr/share/nginx/html/www;
                index   index.html;
                limit_req zone=req_zone;        # 限制請求
                #limit_req zone=req_zone burst=3 nodelay;
        }
}
}

# burst :
#   爆發的意思,這個配置的意思是設定一個大小為5的緩衝區,當有大量請求(爆
#   發)過來時,超過了訪問頻次限制的請求可以先放到這個緩衝區內等待,
#   但是這個等待區裡的位置只有5個,超過的請求會直接報503的錯誤然後返回。

# nodelay :
#    瞬時提供處理(burst + rate)個請求的能力,
#    請求超過(burst + rate)的時候就會直接返回503,永遠不存在請求需要等待的情況
#    如果沒有設定nodelay,則所有請求會依次等待排隊

壓測一下請求限制效果

#安裝壓測工具 [[email protected] ~]$ yum install -y httpd-tools

場景一:burst和nodelay都不加的情況

location / {
        root    /usr/share/nginx/html/www;
        index   index.html;
        limit_req zone=req_zone;
}


# 使用ab測試工具,發起10個併發請求
[[email protected] ~]# ab -n 10 -c 10 http://192.168.1.17/index.html
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.1.17 (be patient).....done


Server Software:        nginx/1.14.1
Server Hostname:        192.168.1.17
Server Port:            80

Document Path:          /index.html
Document Length:        6 bytes

Concurrency Level:      10                      # 10併發個請求
Time taken for tests:   0.011 seconds           
Complete requests:      10                      # 總共請求10次
Failed requests:        9                       #失敗9次
   (Connect: 0, Receive: 0, Length: 9, Exceptions: 0)
Write errors:           0
Non-2xx responses:      9
Total transferred:      3700 bytes
HTML transferred:       1923 bytes
Requests per second:    907.94 [#/sec] (mean)
Time per request:       11.014 [ms] (mean)      # 11ms就壓測結束
Time per request:       1.101 [ms] (mean, across all concurrent requests)
Transfer rate:          328.06 [Kbytes/sec] received

>>>可以看到一共10個請求,9個請求都失敗了。且11ms就完成了壓測



# 檢視 /var/log/nginx/access.log,印證了只有一個請求成功了,其它就是都直接返回了503
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 200 6 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"

場景二:只加burst和不加nodelay的情況

1. 場景一:只加burst和不加nodelay的情況
location / {
                root    /usr/share/nginx/html/www;
                index   index.html;
                limit_req zone=req_zone;        # 限制請求
                #limit_req zone=req_zone burst=3;
        }

# 檢視當前的tcp連線數
[[email protected] ~]$ netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
ESTABLISHED 6
[[email protected] ~]$


# 建立10個連線併發10
[[email protected] ~]$ ab -n 10 -c 5 http://192.168.1.17/index.html
....
Server Software:        nginx/1.14.1
Server Hostname:        192.168.1.17
Server Port:            80

Document Path:          /index.html
Document Length:        6 bytes

Concurrency Level:      10                  # 10併發個請求
Time taken for tests:   0.012 seconds
Complete requests:      10                  # 總共請求10次
Failed requests:        6                   # 失敗了6次
Total transferred:      3250 bytes
HTML transferred:       1302 bytes
Requests per second:    3.32 [#/sec] (mean)
Time per request:       1505.007 [ms] (mean)
Time per request:       301.002 [ms] (mean, across all concurrent requests)
Transfer rate:          1.05 [Kbytes/sec] received
...

>>>> 可以看到一共10個請求,6個請求都失敗了

# ab測試第一秒時ESTABLISHED由4變為6,即建立了2個TCP連線,
# 同時TIME_WAIT=9 表示有伺服器端主動斷開了9個TCP連線,即9個請求被瞬時拒絕
[[email protected] ~]$ netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
ESTABLISHED 6
TIME_WAIT 9
[[email protected] ~]$

# ab測試完,
# TIME_WAIT=10 表示有伺服器端主動斷開了10個TCP連線,增加的1個TIME_WAIT是因為有1個在快取佇列的請求被處理完畢了,所以斷開了連線
[[email protected] ~]$ netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
ESTABLISHED 4
TIME_WAIT 10
[[email protected] ~]$



# 檢視 /var/log/nginx/access.log日誌
192.168.1.13 - - [14/Nov/2018:18:13:53 +0800] "GET /index.html HTTP/1.0" 200 6 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:13:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:13:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:13:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:13:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:13:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:13:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:13:54 +0800] "GET /index.html HTTP/1.0" 200 6 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:13:55 +0800] "GET /index.html HTTP/1.0" 200 6 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:13:56 +0800] "GET /index.html HTTP/1.0" 200 6 "-" "ApacheBench/2.3" "-"

>>>在18:13:53的時候,可以看到壓測第1秒時,成功處理了1個請求,另外有6個請求瞬間返回了503,剩下的4個請求每隔1s處理一次
>>>這是因為設定了burst=3,在伺服器接收到10個併發請求後,先處理1個請求,同時將3個請求放入burst緩衝佇列中,等待處理,
而超過(burst+1)數量的請求就被直接拋棄了,即直接拋棄了6個請求。緩衝區的剩餘3個請求,1秒執行1個。

場景三:加burst和加nodelay的情況

2. 場景二:加burst和加nodelay的情況
location / {
                root    /usr/share/nginx/html/www;
                index   index.html;
                #limit_req zone=req_zone;        # 限制請求
                limit_req zone=req_zone burst=3 nodelay;    #緩衝區大小為3,nodelay瞬時提供處理(burst + rate)個請求
        }


[[email protected] ~]# ab -n 60 -c 20 http://192.168.1.17/index.html
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.1.17 (be patient).....done


Server Software:        nginx/1.14.1
Server Hostname:        192.168.1.17
Server Port:            80

Document Path:          /index.html
Document Length:        6 bytes

Concurrency Level:      20
Time taken for tests:   0.048 seconds
Complete requests:      10
Failed requests:        6                          # 成功了4個請求(3+1)
   (Connect: 0, Receive: 0, Length: 56, Exceptions: 0)
Write errors:           0
Non-2xx responses:      56
Total transferred:      3250 bytes
HTML transferred:       1302 bytes
Requests per second:    359.27 [#/sec] (mean)      # 吞吐量
....

>>> 可以看到一共10個請求,失敗了6個,成功了4個,緩衝區生效了

# 檢視 /var/log/nginx/access.log日誌,可以發現在1s內,伺服器端處理了4個請求(峰值速度:burst+原來的處理速度)。對於剩下的6個請求,直接返回503
[[email protected] ~]$ tail -f /var/log/nginx/access.log 
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 200 6 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 200 6 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 200 6 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 200 6 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"


# 檢視/var/log/nginx/error.log日誌,發現有6個請求被直接拒絕了,沒有延時請求。
[[email protected] ~]$ tail -f /var/log/nginx/error.log 
2018/11/14 17:49:53 [error] 20735#20735: *478 limiting requests, excess: 3.981 by zone "req_zone", client: 192.168.1.13, server: localhost, request: "GET /index.html HTTP/1.0", host: "192.168.1.17"
2018/11/14 17:49:53 [error] 20735#20735: *479 limiting requests, excess: 3.980 by zone "req_zone", client: 192.168.1.13, server: localhost, request: "GET /index.html HTTP/1.0", host: "192.168.1.17"
2018/11/14 17:49:53 [error] 20735#20735: *480 limiting requests, excess: 3.980 by zone "req_zone", client: 192.168.1.13, server: localhost, request: "GET /index.html HTTP/1.0", host: "192.168.1.17"
2018/11/14 17:49:53 [error] 20735#20735: *481 limiting requests, excess: 3.978 by zone "req_zone", client: 192.168.1.13, server: localhost, request: "GET /index.html HTTP/1.0", host: "192.168.1.17"
2018/11/14 17:49:53 [error] 20735#20735: *482 limiting requests, excess: 3.978 by zone "req_zone", client: 192.168.1.13, server: localhost, request: "GET /index.html HTTP/1.0", host: "192.168.1.17"
2018/11/14 17:49:53 [error] 20735#20735: *483 limiting requests, excess: 3.977 by zone "req_zone", client: 192.168.1.13, server: localhost, request: "GET /index.html HTTP/1.0", host: "192.168.1.17"

Nginx請求限制總結

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

Nginx連線頻率限制配置

  • 語法:
# 全域性定義連線限制
句法:	limit_conn_zone key zone-name:size;
預設:	-
語境:	http

# 引用連線限制
句法:	limit_conn zone number;
預設:	-
語境:	http,server,location


# 例項配置[[email protected] /etc/nginx]$ vim nginx.conf
...
limit_conn_zone $binary_remote_addr zone=conn_zone:10m;
server {
        listen  80;
        server_name localhost;
        root    /usr/share/nginx/html/www;
        index   index.html;

        location / {
                root    /usr/share/nginx/html/www;
                index   index.html;
                #limit_req zone=req_zone;
                #limit_req zone=req_zone burst=3 nodelay;
                #同一時間只允許一個客戶端IP連線
                limit_conn conn_zone 1;
        }
}