Вот уже много лет пользователей Nginx мучает один и тот же вопрос: «Как можно ограничить скорость в целом для IP адреса независимо от числа сессий (соединений)? Почему Nginx этого не умеет? Почему разработчики Nginx так упорно не хотят реализовать этот простой функционал?» И ответить мне им нечего, о чём думают разработчики Nginx — не понятно и известно, наверное, только господу богу.
Бороться с этим можно по разному, кто-то использует скрипты на подобие htb.init, кто-то пишет скрипты шейпинга самостоятельно и делится удачным опытом на Хабре, а некоторые и вовсе используют PHP для ограничения скорости отдачи файлов. Только представьте себе, каким будет оверхед и расход памяти, при использовании PHP в подобных целях.
На данный момент Nginx не умеет ограничивать скорость для IP и делает это только в рамках отдельных сессий. Что это означает? Если администратор установил в конфиге ограничение скорости в 100 Кбайт/сек, то создав 10 соединений с сервером можно получить скорость в 1 Мбайт/сек, что никак не вписывается в планы администратора. Достичь желаемого средствами самого Nginx можно лишь установив лимит, например
http {
limit_conn_zone $binary_remote_addr zone=perip:10m;
server {
location /download/ {
limit_conn perip 1;
}
}
}
который не даст создать более 1-го соединения с отдельно взятого IP адреса. Однако на практике использование такого ограничения на сервере имеет мало смысла, ведь за одним IP адресом могут скрываться тысячи пользователей, а при нестабильном сетевом подключении пользователь вообще рискует не получить доступ к файлу.
Но, как оказалось, не всё так мрачно и выход из ситуации есть. Это простой маленький модуль от yaoweibin под названием nginx_limit_speed_module. Давайте рассмотрим как работает этот модуль:
http {
limit_speed_zone one $binary_remote_addr 10m;
server {
location /download/ {
limit_speed one 100k;
}
}
}
Директива limit_speed задаёт суммарную скорость для всех подключений с одного IP адреса. Например, если установлен лимит скорости в 100 Кбайт/сек, а пользователь при этом качает файл в 10 потоков, то скорость скачивания для каждого отдельно взятого потока составит 10 Кбайт/сек (100k/10). Заметьте, это без всяких танцев с шейпингом в Linux и извращений с использованием PHP. Удобно, просто и понятно. На мой взгляд именно так и должно быть, но почему-то этого до сих порт нет в Nginx «из коробки» за что мне хотелось бы кинуть большой такой камешек в огород Nginx.
Для сборки Nginx с этим замечательным модулем достаточно добавить в параметры ./configure такую запись:
--add-module=/путь до папки с модулем/nginx_limit_speed_module
Например:
./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-http_auth_request_module --with-file-aio --with-ipv6 --with-http_spdy_module --add-module=/root/nginx_limit_speed_module --with-cc-opt='-O2 -g -pipe -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic'
Подробно останавливаться на сборке Nginx я не буду, на этому тему уже написано немало статей. В качестве краткого руководства по сборке можно использовать эту инструкцию.
Страница модуля на GitHub: https://github.com/yaoweibin/nginx_limit_speed_module