Ограничение скорости — очень полезная, но часто неправильно понимаемая и неправильно настроенная функция Nginx. Мы можем использовать, чтобы ограничить количество HTTP-запросов, которые пользователь может сделать в данный момент времени. Запрос может быть GET-запросом для домашней страницы простого веб-сайта или POST-запросом для формы входа.
Регулирование трафика можно использовать в целях безопасности, например, для снижения скорости взлома паролей методом грубой силы. Его также можно использовать для защиты от DDOS-атак, ограничивая скорость входящих запросов значениями, типичными для реальных пользователей, и определяя целевые URL-адреса (через журналы). Чаще всего эта функция используется для защиты вышестоящих серверов приложений от перегрузки слишком большим количеством одновременных запросов пользователей.
В этой статье будут представлены основы и расширенная настройка регулирования трафика Nginx, «регулирование трафика» также применяется в Nginx Plus.
Как Nginx ограничивает ток
«Дросселирование трафика» Nginx использует алгоритм дырявого ведра, который широко используется в коммуникациях и компьютерных сетях с коммутацией пакетов для обработки всплесков, когда пропускная способность ограничена. Это похоже на ведро с льющейся в рот водой и протекающим ведром на дне. Если скорость налива воды в горловину ведра больше, чем скорость утечки воды на дно ведра, вода в ведре будет переливаться; аналогично, с точки зрения обработки запроса, вода представляет собой запрос от клиент, а ведро представляет собой ожидание в соответствии с «алгоритмом планирования в порядке поступления» (FIFO). Очередь обработанных запросов, вода, вытекающая со дна ведра, представляет собой запросы, покинувшие буфер для обработки сервером, а вода, вытекающая из ведра, представляет собой запросы, которые отбрасываются и не обрабатываются.
Настройка базового регулирования
«Ограничение трафика» настраивает две основные директивы, limit_req_zone и limit_req, следующим образом: limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server { location /login/ { limit_req zone=mylimit;
proxy_pass http://my_upstream;
}
}
Директива limit_req_zone определяет параметры, связанные с ограничениями трафика, а директива limit_req включает ограничения трафика в том контексте, в котором они появляются (в примере, все запросы к «/login/»). Директива limit_req_zone обычно определяется в блоке HTTP, что делает ее доступной в нескольких контекстах, и для нее требуются следующие три параметра:
Key - 定义应用限制的请求特性。示例中的Nginx变量remote_addr,占用更少的空间)
Zone - 定义用于存储每个IP地址状态以及被限制请求URL访问频率的共享内存区域。保存在内存共享区域的信息,意味着可以在Nginx的worker进程之间共享。定义分为两个部分:通过zone=keyword标识区域的名字,以及冒号后面跟区域大小。16000个IP地址的状态信息,大约需要1MB,所以示例中区域可以存储160000个IP地址。
Rate - 定义最大请求速率。在示例中,速率不能超过每秒10个请求。Nginx实际上以毫秒的粒度来跟踪请求,所以速率限制相当于每100毫秒1个请求。因为不允许”突发情况”(见下一章节),这意味着在前一个请求100毫秒内到达的请求将被拒绝。
当Nginx需要添加新条目时存储空间不足,将会删除旧条目。如果释放的空间仍不够容纳新记录,Nginx将会返回 503状态码(Service Temporarily Unavailable)。另外,为了防止内存被耗尽,Nginx每次创建新条目时,最多删除两条60秒内未使用的条目。
Директива limit_req_zone устанавливает параметры ограничения трафика и зон разделяемой памяти, но фактически не ограничивает скорость запросов. Поэтому вам нужно применить ограничение трафика к определенному местоположению или блоку сервера, добавив директиву limit_req. В приведенном выше примере мы ограничиваем трафик запросами /login/.
Каждый IP-адрес теперь ограничен 10 запросами в секунду для /login/, точнее, URL-адрес не может быть запрошен в течение 100 мс после предыдущего запроса. обрабатывать всплески
Что, если мы получим 2 запроса за 100 мс? Для второго запроса Nginx вернет клиенту код состояния 503. Это может быть не тот результат, который нам нужен, поскольку приложения имеют тенденцию к скачкообразному характеру. Вместо этого мы хотим буферизовать любые лишние запросы и своевременно их обрабатывать. Обновим конфигурацию и воспользуемся параметром Burst в limit_req:
location /login/ { limit_req zone=mylimit burst=20; proxy_pass http://my_upstream; }
Параметр Burst определяет, сколько запросов клиент может сделать сверх скорости, указанной в зоне (в примере область mylimit, скорость ограничена 10 запросами в секунду или одним запросом каждые 100 миллисекунд). Запросы, которые поступили в течение 100 мс после последнего запроса, будут поставлены в очередь, и мы устанавливаем размер очереди равным 20.
Это означает, что если с заданного IP-адреса будет отправлен 21 запрос, Nginx немедленно отправит первый запрос на вышестоящую ферму серверов, а затем поставит в очередь оставшиеся 20 запросов. Затем запрос в очереди перенаправляется каждые 100 мс, и Nginx возвращает клиенту 503, только если входящий запрос содержит более 20 запросов в очереди. Нет задержки в очереди
Настройка параметра пакетной передачи сделает связь более плавной, но может оказаться непрактичной, поскольку сайт может выглядеть медленным. В приведенном выше примере 20-й пакет в очереди должен ждать 2 секунды для пересылки, после чего ответ клиенту может быть бесполезен. Чтобы решить эту ситуацию, добавьте параметр nodelay после параметра Burst:
location /login/ { limit_req zone=mylimit burst=20 nodelay;
proxy_pass http://my_upstream;
}
С параметром nodelay Nginx по-прежнему будет распределять позиции в очереди на основе параметра пакета и применять настроенное ограничение скорости вместо очистки очереди запросов, ожидающих пересылки. И наоборот, когда запрос поступает «слишком рано», Nginx перенаправит запрос, как только сможет выделить место в очереди. Помечает эту позицию в очереди как «занятую» и не будет освобождаться для другого запроса до тех пор, пока не пройдет некоторое время (в данном примере — через 100 миллисекунд).
Предположим, как упоминалось ранее, что в очереди имеется 20 пустых слотов и одновременно поступает 21 запрос с заданного IP-адреса. Nginx немедленно перенаправит 21 запрос и пометит 20 позиций, занятых в очереди, а затем освободит позицию каждые 100 миллисекунд. Если одновременно поступит 25 запросов, Nginx немедленно переадресует 21 из них, отметит 20 занятых позиций в очереди и вернет код состояния 503, чтобы отклонить оставшиеся 4 запроса.
Теперь предположим, что через 101 миллисекунду после пересылки первого набора запросов одновременно поступило еще 20 запросов. Только один слот в очереди будет освобожден, поэтому Nginx перенаправляет один запрос и возвращает код состояния 503, чтобы отклонить остальные 19 запросов. Если до поступления 20 новых запросов прошло 501 миллисекунда, освобождается 5 слотов, поэтому Nginx немедленно пересылает 5 запросов и отклоняет остальные 15.
Эффект эквивалентен «лимиту трафика» в 10 запросов в секунду. Параметр nodelay полезен, если вы хотите применить «регулирование трафика», не ограничивая допустимый интервал между двумя запросами.
Примечание. Для большинства развертываний мы рекомендуем использовать параметры Burst и Nodelay для настройки директивы limit_req. Пример расширенной конфигурации
Комбинируя базовое «регулирование трафика» с другими функциями Nginx, мы можем добиться более тонкого регулирования трафика.
белый список
В следующем примере показано, как применить «лимит трафика» для любого запроса, которого нет в белом списке:
geo $limit { default 1; 10.0.0.0/8 0; 192.168.0.0/64 0; }
map limit_key { 0 ""; 1 $binary_remote_addr; }
limit_req_zone $limit_key zone=req_zone:10m rate=5r/s;
server { location / { limit_req zone=req_zone burst=10 nodelay;
# ...
}
}
В этом примере используются директивы geo и map. Геоблок присвоит значение 0 переменной $limit, соответствующей IP-адресам в белом списке, и значение 1 другим, которых нет в белом списке. Затем мы используем карту для преобразования этих значений в ключи следующим образом:
如果
Значение переменной , переменной limit_key будет присвоена пустая строка если Значение переменной
,limit_key变量将被赋值为客户端二进制形式的IP地址 两个指令配合使用,白名单内IP地址的$limit_key变量被赋值为空字符串,不在白名单内的被赋值为客户端的IP地址。当limit_req_zone后的第一个参数是空字符串时,不会应用“流量限制”,所以白名单内的IP地址(10.0.0.0/8和192.168.0.0/24 网段内)不会被限制。其它所有IP地址都会被限制到每秒5个请求。
Директива limit_req применяет ограничение к блоку местоположения /, допуская пакеты до 10 пакетов сверх настроенного ограничения, и пересылка не задерживается. location содержит несколько директив limit_req
Мы можем настроить несколько директив limit_req в блоке местоположения. Когда применяются все лимиты, соответствующие заданному запросу, это означает, что будет применен самый ограничивающий лимит. Например, если несколько инструкций имеют указанные задержки, будет использована самая длинная задержка. Аналогично, запросы, затронутые некоторыми директивами, отклоняются, даже если другие директивы разрешают их прохождение.
Расширение предыдущего примера применения «ограничения трафика» к IP-адресам в белом списке:
http { # ...
limit_req_zone $limit_key zone=req_zone:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=req_zone_wl:10m rate=15r/s;
server {
# ...
location / {
limit_req zone=req_zone burst=10 nodelay;
limit_req zone=req_zone_wl burst=20 nodelay;
# ...
}
}
}
IP-адреса в белом списке не будут соответствовать первому «лимиту трафика», но будут соответствовать второму req_zone_wl и ограничены 15 запросами в секунду. IP-адреса, не входящие в белый список, соответствуют обоим ограничениям, поэтому применяется тот, у которого более сильное ограничение: 5 запросов в секунду. Настройка связанных функций
Ведение журнала По умолчанию Nginx регистрирует запросы, которые задерживаются или отбрасываются из-за ограничения трафика, следующим образом:
2015/06/13 04:20:00 [error] 120315#0: *32086 limiting requests, excess: 1.000 by zone "mylimit", client: 192.168.1.2, server: nginx.com,
request: "GET / HTTP/1.0", host: "nginx.com"
Поля, включенные в записи журнала:
limiting requests - 表明日志条目记录的是被“流量限制”请求
excess - 每毫秒超过对应“流量限制”配置的请求数量
zone - 定义实施“流量限制”的区域
client - 发起请求的客户端IP地址
server - 服务器IP地址或主机名
request - 客户端发起的实际HTTP请求
host - HTTP报头中host的值
По умолчанию Nginx регистрирует отклоненные запросы на уровне ошибки, как показано в [error] в приведенном выше примере (Ngin регистрирует отложенные запросы на более низком уровне, обычно на информационном уровне). Чтобы изменить уровень ведения журнала Nginx, используйте директиву limit_req_log_level. Здесь мы устанавливаем уровень ведения журнала для отклоненных запросов, чтобы предупредить:
location /login/ { limit_req zone=mylimit burst=20 nodelay; limit_req_log_level warn;
proxy_pass http://my_upstream;
}
Код ошибки отправлен клиенту
Как правило, когда клиент превышает настроенный лимит трафика, Nginx отвечает кодом состояния 503 (служба временно недоступна). Вы можете использовать команду limit_req_status для установки других кодов состояния (например, код состояния 444 ниже):
location /login/ { limit_req zone=mylimit burst=20 nodelay; limit_req_status 444; }
Указанный адрес отклоняет все запросы
Если вы хотите запретить все запросы к указанному URL-адресу, а не просто ограничить скорость, просто настройте директиву deny all в блоке location:
location /foo.php { deny all; }
Суммировать
В предыдущей статье были рассмотрены многие функции «регулирования трафика», предоставляемые Nginx и Nginx Plus, в том числе настройка скорости запросов для различных вариантов размещения HTTP-запросов, а также настройка параметров пакета и узла для «регулирования трафика». Он также охватывает расширенную настройку применения различного «регулирования трафика» для внесения в белый и черный списки клиентских IP-адресов, объясняя, как регистрировать отклоненные и задержанные запросы.
END