Friday, 3 April 2009

ngx_http_limit_var_module.c

Модуль ngx_http_limit_var_module (патч на для nginx >= 0.7.52) позволяет ограничить число запросов для заданного ключа (ключем может быть uri и кука, может быть ip или любая строка состоящая как из простых значений, так и из переменных). Основное отличие от ngx_http_limit_req_module заключается в предоставлении информации о необходимости ограничений через механизм переменных, а это позволяет сократить количество выполнений тяжелой части запроса при обработке клиента (или включить кеширование), и в не совсем честном алгоритме.

Пример конфигурации
http {
    limit_var  $remote_addr  zone=one:500000  rate=10r/s;

    ...

    server {

        ...

        location /search/ {
            if ($limit_var_one) {
                proxy_cache cache_zone;
            }
        }
Описание директив

syntax: limit_var $переменная zone=название:количество_состояний rate=скорость
default: нет
context: http

Директива описывает зону в которой будут храниться состояния и максимальная скорость для одного состояния. Так же будет создана переменная вида $limit_var_название, в которой будет текущая скорость в r/s при условии что она выше чем ограничения для этого имени.

Скорость задаётся в запросах в секунду.

Описание алгоритмов

В алгоритме хранения могут быть коллизии, когда для нескольких разных ключей используется одно и тоже значение. Их количество зависит только от количества хранимых состояний — чем оно больше, тем их меньше.

Write on: 22:27 | 12 comments | | tags: , , , , | permalink |
Add post to:   Delicious Reddit Slashdot Digg Technorati Google


Add comment

Comments

asd 3.04.2009 23:35

Ты, воистину, велик!

reply
Петр 4.04.2009 0:13

Модуль — это очень здорово! Патчи это тоже хорошо, но модули лучше ;)

reply
Kirill A. Korinskiy 4.04.2009 23:40

У меня все мои модули очень сильно связаны, и мне проще выкладывать diff до оригинальной версии :)

Да и править исходный код, оригинальный, местами, я себе разрешаю.

reply
Alrond 6.04.2009 4:10

Кирилл, а ограничение реально посекундное? Потому как оригинальный limit_req_module работает не так, как заявлено

А еще вопрос — память увеличивается динамически или сразу выдается по количеству состояний? И когда их число уменьшается, память освобождается или это на совести аллокатора? Интересно кстати как в этом случае тот патч с openbsd аллокатором себя поведет…есть повод потестировать :)
Я так понимаю что память общая для всех воркеров, как ты и любишь делать ;)

И $limit_var_one можно использовать на любых уровнях вложенности?
Можно ли на одном уровне использовать несколько limit_var? Например один по $remote_addr (кстати, наверное лучше для этономии памяти использовать $binary_remote_addr) а другой по $server_name (небольшая защита от перегрузок для бекенда)

А есть идеи как это прикрутить, чтобы работало с двойными условиями?
Например, чтобы ограничить количество коннектов для всех, кроме группы VIP персон (определяемых например по geo модулю), то сейчас можно так организовать:

location / {
set $access 1;
if ($limit_var_one) {
set $access 0;
}
if ($user_vip) {
set $access 0;
}
if ($access) {
proxy_pass http://127.0.0.1:8080/;
include proxy.conf;
}
}

Блин, как же не хватает что-то подобного:
if (!$limit_var_one || $user_vip) {
proxy_pass http://127.0.0.1:8080/;
include proxy.conf;
}

reply

Comment form for «ngx_http_limit_var_module.c»

Required. 30 chars of fewer.

Required.

Kirill A. Korinskiy 6.04.2009 4:42

Кирилл, а ограничение реально посекундное? Потому как оригинальный limit_req_module работает не так, как заявлено

Не совсем так. Сейчас ограничение сделано в виде счатчика. Т.е. если ты уставаливаешь не больше 10 запросов в секунду оно будет пропускать не больше 10 запросов в секунду, сбрасывая за каждую секунду простоя 1 запрос.

т.е. у нас 10 r/s ограничение, и мы делаем за секунду 20 запросов. Это означает что первые 9 штук пройду, а остальные 11 будут привышением. Если мы на следующей секунде счетчик плохих запросов уменьшится на 1 и станет равным 10. На следующий 9. На следующий 8 и теперь мы сможем сделать еще один запрос.

Причина такого алгоритма очень проста — оптимизация по памяти.

А еще вопрос — память увеличивается динамически или сразу выдается по количеству состояний? И когда их число уменьшается, память освобождается или это на совести аллокатора? Интересно кстати как в этом случае тот патч с openbsd аллокатором себя поведет…есть повод потестировать :) Я так понимаю что память общая для всех воркеров, как ты и любишь делать ;)

Память выделяется одним куском. И не изменяется всю жизнь nginx.

И $limit_var_one можно использовать на любых уровнях вложенности?

Да, но только после определения.

Можно ли на одном уровне использовать несколько limit_var? Например один по $remote_addr (кстати, наверное лучше для этономии памяти использовать $binary_remote_addr) а другой по $server_name (небольшая защита от перегрузок для бекенда)

Можно хоть 16 переменных сделать, главное что бы памяти хватило. Касательно remoteaddr — модулю всеравно. Он всеравно будет считать 32 битных хеш, т.е. ограничение по памяти зависит от колличество состояний а не размера переменной или длини строки с ними ;)

Надо понимать что эти структуры данных кушают очень мало памяти, но имеют коллизии, и коллизия будет считаться как счетчик на двоих. Я постораюсь в ближайшее время выложжить тесты относительно колличетсва состояний, необходимой памяти и процента коллизий, но мне нужно время.

А есть идеи как это прикрутить, чтобы работало с двойными условиями?

операторы || и && мне предется, все-таки делать в if’ах и я от этого не денусь некуда, следите за патчами :)

reply
Alrond 6.04.2009 5:20

Сейчас ограничение сделано в виде счатчика. Т.е. если ты уставаливаешь не больше 10 запросов в секунду оно будет пропускать не больше 10 запросов в секунду, сбрасывая за каждую секунду простоя 1 запрос.
т.е. у нас 10 r/s ограничение, и мы делаем за секунду 20 запросов. Это означает что первые 9 штук пройду, а остальные 11 будут привышением. Если мы на следующей секунде счетчик плохих запросов уменьшится на 1 и станет равным 10. На следующий 9. На следующий 8 и теперь мы сможем сделать еще один запрос.
Причина такого алгоритма очень проста — оптимизация по памяти.

То есть если я правильно понимаю, то сделав эти 20 запросов за одну секунду, юзер сможет снова вернуться только через 11 секунд. Хотя за эти 10 секунд ему позволительно сделать 11*10 запросов. То есть поведение немного настораживающее…
Может ввести дополнительную настройку, позволяющую не учитывать все, что сверху, то есть сбрасывать сразу же после этой секунды. Тогда появится необходимый эффект, что именно не больше 10 запросов в секунду. все что сверху — долой, но зато на следующую секунду он может опять сделать до 10. Как-то так:
limit_var $remote_addr zone=one:500000 rate=10r/s zero=1;
И память кстати будет больше экономиться

reply
Денис 6.04.2009 8:53

пытаюсь наложить патч ngx_http_limit_var_module-0.1.patch после патчей nginx-catap-0.7.47.352.084bd34.diff — ошибку пишет. Можно выложить на сайте коммулятивный пачт для nginx 0.7.47 ?

reply
Kirill A. Korinskiy 6.04.2009 13:11

Сделал патч для v0.7.47.352.084bd34.

В будущем я буду выкладывать два патча, для последней версии оригинального и последней версии своего патча, спасибо.

reply
Kirill A. Korinskiy 6.04.2009 13:07

Угу, это я понимаю, и постораюсь обновить патч сегодня-завтра (добавить опцию сколько запросов списывать за секунду).

На самом деле такой алгоритм очень хорошо защитит от ab и подобных штук ;)

reply
Kirill A. Korinskiy 6.04.2009 18:05

Обновил описания алгоритма — остались вопросы?

reply
csdoc 6.04.2009 11:21

зачем считать 32-битный хеш от $remote_addr, если можно использовать $binary_remote_addr, эта переменная равна адресу клиента в бинарном виде, длина её значения всегда 4 байта;

reply
Kirill A. Korinskiy 6.04.2009 13:06

Все очень просто. $remote_addr это частный и не интереснный источник сессии клиента. Куда интересней, например, кука.

reply