![Ограничение скорости nginx не работает так, как ожидалось](https://rvso.com/image/1684440/%D0%9E%D0%B3%D1%80%D0%B0%D0%BD%D0%B8%D1%87%D0%B5%D0%BD%D0%B8%D0%B5%20%D1%81%D0%BA%D0%BE%D1%80%D0%BE%D1%81%D1%82%D0%B8%20nginx%20%D0%BD%D0%B5%20%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0%D0%B5%D1%82%20%D1%82%D0%B0%D0%BA%2C%20%D0%BA%D0%B0%D0%BA%20%D0%BE%D0%B6%D0%B8%D0%B4%D0%B0%D0%BB%D0%BE%D1%81%D1%8C.png)
Я пытаюсь ограничить скорость соединений nginx, однако это, похоже, не работает так, как ожидалось. Я пробовал тестировать его, используя 2 и 10 запросов в секунду.
Сначала 2 запроса в секунду
limit_req_zone $binary_remote_addr zone=myzone:10m rate=2r/s;
limit_req_status 429;
server {
listen *:80;
server_name 172.23.97.94;
root /var/www/html;
index index.html;
location / {
limit_req zone=myzone;
try_files $uri $uri/ =404;
}
}
Тестирование через curl:
for i in {1..2}; do curl -I -s "http://172.23.97.94" | head -n 1; done
HTTP/1.1 200 OK
HTTP/1.1 429 Too Many Requests
Access.log подтверждает, что одновременно поступает только 2 запроса, однако второй запрос получает код 429:
172.23.106.65 - - [08/Feb/2023:17:10:35 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.68.0"
172.23.106.65 - - [08/Feb/2023:17:10:35 +0000] "HEAD / HTTP/1.1" 429 0 "-" "curl/7.68.0"
Но если я сделаю тот же запрос с задержкой в 0,5 секунды, то все пройдет нормально:
for i in {1..2}; do curl -I -s "http://172.23.97.94" | head -n 1; sleep 0.5; done
HTTP/1.1 200 OK
HTTP/1.1 200 OK
Во-вторых, 10 запросов в секунду
limit_req_zone $binary_remote_addr zone=myzone:10m rate=10r/s;
limit_req_status 429;
server {
listen *:80;
server_name 172.23.97.94;
root /var/www/html;
index index.html;
location / {
limit_req zone=myzone;
try_files $uri $uri/ =404;
}
}
Тестирование через curl:
for i in {1..10}; do curl -I -s "http://172.23.97.94" | head -n 1; done
HTTP/1.1 200 OK
HTTP/1.1 429 Too Many Requests
HTTP/1.1 429 Too Many Requests
HTTP/1.1 429 Too Many Requests
HTTP/1.1 429 Too Many Requests
HTTP/1.1 429 Too Many Requests
HTTP/1.1 429 Too Many Requests
HTTP/1.1 429 Too Many Requests
HTTP/1.1 429 Too Many Requests
HTTP/1.1 429 Too Many Requests
Access.log подтверждает, что одновременно происходит только 10 подключений, но только первое получает 200:
172.23.106.65 - - [08/Feb/2023:17:14:53 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.68.0"
172.23.106.65 - - [08/Feb/2023:17:14:53 +0000] "HEAD / HTTP/1.1" 429 0 "-" "curl/7.68.0"
172.23.106.65 - - [08/Feb/2023:17:14:53 +0000] "HEAD / HTTP/1.1" 429 0 "-" "curl/7.68.0"
172.23.106.65 - - [08/Feb/2023:17:14:53 +0000] "HEAD / HTTP/1.1" 429 0 "-" "curl/7.68.0"
172.23.106.65 - - [08/Feb/2023:17:14:53 +0000] "HEAD / HTTP/1.1" 429 0 "-" "curl/7.68.0"
172.23.106.65 - - [08/Feb/2023:17:14:53 +0000] "HEAD / HTTP/1.1" 429 0 "-" "curl/7.68.0"
172.23.106.65 - - [08/Feb/2023:17:14:53 +0000] "HEAD / HTTP/1.1" 429 0 "-" "curl/7.68.0"
172.23.106.65 - - [08/Feb/2023:17:14:53 +0000] "HEAD / HTTP/1.1" 429 0 "-" "curl/7.68.0"
172.23.106.65 - - [08/Feb/2023:17:14:53 +0000] "HEAD / HTTP/1.1" 429 0 "-" "curl/7.68.0"
172.23.106.65 - - [08/Feb/2023:17:14:53 +0000] "HEAD / HTTP/1.1" 429 0 "-" "curl/7.68.0"
Но если я сделаю тот же запрос со сном в 0,01 секунды, то некоторые из них будут 200, а другие — 429:
for i in {1..10}; do curl -I -s "http://172.23.97.94/device/1" | head -n 1; sleep 0.01; done
HTTP/1.1 200 OK
HTTP/1.1 429 Too Many Requests
HTTP/1.1 429 Too Many Requests
HTTP/1.1 429 Too Many Requests
HTTP/1.1 429 Too Many Requests
HTTP/1.1 200 OK
HTTP/1.1 429 Too Many Requests
HTTP/1.1 429 Too Many Requests
HTTP/1.1 429 Too Many Requests
HTTP/1.1 200 OK
Я что-то делаю не так? Или ограничение скорости просто не работает так, как ожидалось?
решение1
Я тоже столкнулся с этой проблемой — 10r/s — это фактически один запрос в каждую десятую долю секунды.
Вот соответствующий пост на Stack Overflow:https://stackoverflow.com/questions/62262540/ideal-config-for-nginx-rate-limiting
Вот статья, на которую ссылаются авторы nginx, говорящие об этом:https://www.nginx.com/blog/rate-limiting-nginx/
Мне это кажется плохим решением, поскольку часто бывает так, что одна страница загружает несколько ajax-запросов, а при таком подходе они задерживаются при любом ограничении скорости.
Из статьи следует, что есть способы обойти это с помощью опции «nodelay» и двухэтапного ограничения скорости.
limit_req zone=ip burst=12 delay=8;
Который разрешает 8 запросов одновременно, затем откладывает их выполнение до тех пор, пока в очереди не наберется 12, а затем начинает отклонять их.