# теория · шаг 2 из 6
burst, nodelay и limit_conn
Коротко
Голый limit_req отвергает всё лишнее мгновенно. burst добавляет к ведру
очередь, nodelay пропускает очередь сразу, delay=N - середина.
- Замер при
rate=1r/sи десяти одновременных: без burst - 1 успех, сburst=3- 4, и трое из них ждут 1, 2 и 3 секунды. - С
nodelayте же четверо проходят мгновенно, а лимит всё равно считается. delay=2пропускает сразу двоих сверх нормы, остальных ставит в очередь.limit_connсчитает не частоту, а одновременность: пять скачиваний приlimit_conn 2дают 2 успеха и 3 отказа.
Если различаешь burst, nodelay и delay= - листай до «Теперь сам».
Сначала ответь сам
rate=1r/s, burst=3, десять одновременных запросов. Сколько пройдёт и как
быстро?
for i in $(seq 10); do curl -s -o /dev/null -w '%{http_code} %{time_total}\n' http://localhost/api/x & done; wait | sort -k2 -n
200 0.006
200 0.995
200 1.985
200 2.983
503 0.001
503 0.001
503 0.001
503 0.001
503 0.001
503 0.003
Четыре успеха: один сразу и трое из очереди - 0.99, 1.98, 2.98 секунды.
Три запроса ждали примерно одну, две и три секунды. Ведро углубили, но течёт оно с прежней
скоростью, и очередь рассасывается по одному в секунду.
На что это похоже
Тот же турникет, но перед ним поставили загон на трёх человек. Толпа из десяти: один проходит сразу, трое встают в загон и проходят по очереди, шестерым не хватило места - разворачиваются.
nodelay - это когда охранник пропускает всех, кто поместился в загон, разом, а
потом закрывает створку на соответствующее время. Пропускная способность та же,
ожидание другое.
Механизм: три режима одной директивы
Правило
limit_req zone=one burst=20 nodelay;
- без
burst- только для того, что действительно должно идти по одному: отправка кода подтверждения, дорогая выгрузка; burstбезnodelay- когда клиенту лучше подождать, чем получить ошибку: фоновые интеграции, выгрузки по расписанию;burstсnodelay- для живых людей: браузер, открывающий страницу с десятком запросов, не должен ждать секунды на ровном месте.
Для интерфейса почти всегда нужен третий вариант.
Разбор: nodelay не отменяет лимит
200 0.002
200 0.002
200 0.005
200 0.010
503 0.001
503 0.001
503 0.001
503 0.001
503 0.002
503 0.002
Четыре успеха, как и с обычным burst, но время у всех около нуля - - но без единой секунды ожидания.
Пропускная способность не изменилась: ведро всё так же вмещает 1 + burst и
течёт со скоростью rate.
Разница в том, когда клиент узнаёт об отказе. С nodelay он получает свои
четыре ответа мгновенно и упирается в лимит на пятом; без nodelay он получает
все четыре, но последний - через три секунды.
Разбор: delay=N - середина
limit_req zone=one burst=5 delay=2;
200 0.001
200 0.001
200 0.006
200 0.992
200 1.991
200 2.989
503 0.001
503 0.001
Читается так: первые два сверх нормы пропускаем мгновенно, остальных до
burst ставим в очередь, что не поместилось - отвергаем. Это разумное
умолчание для публичного API: короткая пачка проходит без задержки, длинная
притормаживается, совсем длинная отвергается.
Разбор: limit_conn - про другое
limit_req считает частоту, limit_conn - сколько соединений клиент держит
прямо сейчас:
limit_conn_zone $binary_remote_addr zone=conn:10m;
limit_conn conn 2;
for i in 1 2 3 4 5; do curl -s -o /dev/null -w '%{http_code}\n' http://localhost/downloads/big & done; wait | sort | uniq -c
2 200
3 503
[error] limiting connections by zone "conn"
На быстрых ответах эта директива почти не срабатывает: соединение живёт
миллисекунды. Смысл появляется там, где ответ долгий - скачивания, выгрузки,
потоковые ответы. Пара limit_conn плюс limit_rate из одиннадцатого раздела -
типовая защита канала.
Что ломается без этого
Лимит на статику. limit_req на location / вместе с картинками и css
означает, что открытие страницы с сорока файлами съедает лимит одним человеком.
Ограничивают то, что дорого: логин, поиск, отправку форм, тяжёлые отчёты.
Один лимит на всё. Зона одна, а маршруты разные: странице каталога уместен
burst=20 nodelay, а /api/login - жёсткий rate=1r/s без burst. Зон делают
несколько, по смыслу, и включают в нужных местах.
Лимит применяется на своём уровне. Директива limit_req наследуется по
обычным правилам, но, как и allow, замещается целиком: своя строка в location
отменяет унаследованные.
Зачем это в работе
Практический рецепт для типового сайта:
limit_req_zone $binary_remote_addr zone=general:10m rate=30r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
limit_conn_zone $binary_remote_addr zone=conn:10m;
limit_req zone=general burst=50 nodelay;
limit_conn conn 20;
location = /api/login {
limit_req zone=login burst=3 nodelay;
limit_req_status 429;
proxy_pass http://app:8000;
}
Общий щедрый лимит на весь сайт плюс жёсткий на точку входа. И limit_req_status
429 там, где с клиентом разговаривает программа, а не человек.
Теперь сам
Поставили limit_req zone=one burst=100 nodelay; при rate=1r/s - «чтобы
никого не отрезать». Через месяц бот перебирает пароли, и лимит его не
останавливает. Почему?
Потому что burst=100 разрешает пачку в сто запросов до начала ограничений, а
боту этого хватает: он делает сотню попыток мгновенно, потом ждёт и повторяет.
Большой burst уместен там, где пачка нормальна (страница с десятками файлов), и
вреден на точке входа. На /api/login ставят маленький burst и код 429.
Главное
burst углубляет ведро, но не ускоряет вытекание: замер при rate=1r/s и
десяти одновременных даёт 4 успеха и с burst=3, и с burst=3 nodelay -
разница только в том, ждут ли трое из них по 1, 2 и 3 секунды. delay=N
пропускает сразу N сверх нормы, а остальных ставит в очередь. limit_conn
считает не частоту, а одновременность, и осмыслен на долгих ответах: пять
скачиваний при limit_conn 2 дают два успеха. Зон делают несколько - щедрую на
весь сайт и жёсткую на точку входа.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий