Ограничение скорости 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 ответ

Я тоже столкнулся с этой проблемой — 10р/с — это действительно один запрос на каждую десятую долю секунды.

Вот связанное сообщение о переполнении стека: 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, а затем начинает их отклонять.

Другие вопросы по тегам