Ограничение скорости nginx не работает так, как ожидалось

Ограничение скорости nginx не работает так, как ожидалось

Я пытаюсь ограничить скорость соединений 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, а затем начинает отклонять их.

Связанный контент