И так, сервис зеркалирования работает. Ранее я обратил внимание на возможность получения пользователем "битого" файла, который в момент обращения к нему находится в процессе скачивания Wget. Решение - проверка состояния файла перед его выдачей пользователю и выдача уведомления о неоконченной загрузке, буде такое обнаружиться. После продолжительных раскопок на просторах интернета и сайта разработчика Nginx, в частности, ничего лучшего, чем применения для этих целей встроенного Perl не придумал.
Да, я в курсе того, что встроенный в Nginx Perl исполняется как блокирующий процесс и резко снизит отзывчивость при отдаче контента. Утешает то, что происходить это будет только в момент запроса файла. В любом случае - это легче, чем Apache.
В пакете Nginx для Debian Lenny поддержка Perl не предусмотрена. Стало быть, нужно собрать свой пакет.
Инсталлируем минимально необходимый набор пакетов Perl (если он уже не стоит в более расширенном варианте, разумеется):
# aptitude install perl-base libperl-dev
Устанавливаем утилиту, что соберет нам после сборки из исходных кодов пакет, готовый для установки в систему:
# aptitude install checkinstall
Инсталлятор потянет кроме указанных продуктов ещё как минимум build-essential, bzip, dpkg-dev, g++, make.
Устанавливаем пакеты, требующиеся свежему Nginx:
# aptitude install libpcre3 libpcre3-dev libssl-dev zlib1g-dev
Скачиваем дистрибутив по ссылке полученной с сайта разработчиков:
# cd /usr/src
# wget http://sysoev.ru/nginx/nginx-0.8.53.tar.gz
# wget http://sysoev.ru/nginx/nginx-0.8.53.tar.gz
Распаковываем архив. Переходим в корень директории с исходными кодами:
# tar -xvf nginx-0.8.53.tar.gz
# cd ./nginx-0.8.53
# cd ./nginx-0.8.53
Смотрим, не придумали ли разработчики чего нового, нет ли несовместимых с прежней конфигурацией ключей:
# ./configure --help
Проверяем, удовлетворяет ли наша система зависимостям устанавливаемого программного обеспечения:
# ./configure --prefix=/var/lib/nginx --sbin-path=/usr/sbin --conf-path=/etc/nginx/nginx.conf --pid-path=/var/run/nginx.pid --lock-path=/var/lock/nginx.lock --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log \
--user=www-data --group=www-data \
--with-cc-opt="-I /usr/include" --with-ld-opt="-L /usr/lib" \
--http-proxy-temp-path=/var/cache/nginx \
--with-http_realip_module --with-http_sub_module --with-http_gzip_static_module --with-http_stub_status_module --with-http_perl_module --with-http_ssl_module \
--without-mail_pop3_module --without-mail_imap_module --without-mail_smtp_module --without-http_geo_module --without-http_ssi_module --without-http_empty_gif_module --without-http_browser_module --without-http_memcached_module
--user=www-data --group=www-data \
--with-cc-opt="-I /usr/include" --with-ld-opt="-L /usr/lib" \
--http-proxy-temp-path=/var/cache/nginx \
--with-http_realip_module --with-http_sub_module --with-http_gzip_static_module --with-http_stub_status_module --with-http_perl_module --with-http_ssl_module \
--without-mail_pop3_module --without-mail_imap_module --without-mail_smtp_module --without-http_geo_module --without-http_ssi_module --without-http_empty_gif_module --without-http_browser_module --without-http_memcached_module
Вывод конфигуратора "говорящий" и явно даёт понят, успешно ли прошла процедура.
Компилируем продукт:
# checkinstall -D --install=no --pkggroup=Nginx --pkgname=nginx-perl --pkgversion=0.8.53 --pkgrelease=1 --pkgsource=sysoev.ru --maintainer=your@mail.com make install
На выходе утилиты получаем готовый пакет, уложенный в директории с исходными кодами, если не было иных указаний на путь сохранения. Устанавливаем полученный пакет:
Done. The new package has been saved to
/usr/src/nginx-0.8.53/nginx-perl_0.8.53-1_i386.deb
You can install it in your system anytime using:
dpkg -i nginx-perl_0.8.53-1_i386.deb
/usr/src/nginx-0.8.53/nginx-perl_0.8.53-1_i386.deb
You can install it in your system anytime using:
dpkg -i nginx-perl_0.8.53-1_i386.deb
Останавливаем и удаляем пакет работающего ранее Nginx:
# /etc/init.d/nginx stop
# aptitude remove nginx
# aptitude remove nginx
Устанавливаем подготовленный нами пакет Nginx+Perl и запускаем сервер:
# dpkg -i ./nginx-perl_0.8.53-1_i386.deb
# /etc/init.d/nginx start
# /etc/init.d/nginx start
С учётом того, что мы уже имели установленный и настроенный Nginx, после его обновления мы должны получить всё так же работоспособный сервис, который предстоит дополнить и расширить функционал.
Создаём домик для скриптов Perl:
# mkdir -p /var/lib/nginx/perl
# touch /var/lib/nginx/perl/check-load.pl
# chown -R www-data:www-data /var/lib/nginx/perl
# touch /var/lib/nginx/perl/check-load.pl
# chown -R www-data:www-data /var/lib/nginx/perl
Теперь дополним файлы конфигурации Nginx конструкциями, с помощью Perl осуществляющими необходимую нам проверку:
# cat /etc/nginx/sites-available/mirror.local
# Указываем Nginx место расположение дополнительных модулей Perl и осуществляем загрузку нашего самописного модуля
perl_modules /var/lib/nginx/perl;
perl_require check-load.pl;
server {
location / {
# Размещаем вызов процедуры Perl в самом начале обработки обращения к ресурсу
perl checker::handler;
....
}
}
perl_modules /var/lib/nginx/perl;
perl_require check-load.pl;
server {
location / {
# Размещаем вызов процедуры Perl в самом начале обработки обращения к ресурсу
perl checker::handler;
....
}
}
Пишем модуль с процедурой проверки ресурса:
# cat /var/lib/nginx/perl/check-load.pl
package checker;
use nginx;
use File::stat;
sub handler {
my $resource = shift;
# Узнаём время последнего изменения (модификации) файла
my $entry = stat($resource->filename);
my $mtime = $entry->mtime;
# Отлавливаем ситуацию, когда запрошенный ресурс - файл, и создан он менее минуты назад
if ( -f ($resource->filename) && (time() - $mtime) < 60 ) {
return 403;
} else {
# ...иначе - передаём управление Nginx
return DECLINED;
}
}
1;
use nginx;
use File::stat;
sub handler {
my $resource = shift;
# Узнаём время последнего изменения (модификации) файла
my $entry = stat($resource->filename);
my $mtime = $entry->mtime;
# Отлавливаем ситуацию, когда запрошенный ресурс - файл, и создан он менее минуты назад
if ( -f ($resource->filename) && (time() - $mtime) < 60 ) {
return 403;
} else {
# ...иначе - передаём управление Nginx
return DECLINED;
}
}
1;
Как видно, "отлов" загружаемого файла производится на основе данных времени модификации файла. Wget, в процессе загрузки файла, с каждой новой порцией данных, обновляет время модификации такового до текущего. На минимальной разнице между временем модификации файла и текущим временем мы и работаем.
В начале мне думалось нагородить конструкции информирования пользователя о состоянии запрашиваемого файла, по примеру, приведённому после этого абзаца. Но после, в голову пришло, что только человек сможет понять представленный ему текст, в то время как автоматические загрузчики жестоко обмануться, приняв информационное сообщение за содержимое отдаваемого файла, и решил ограничится ответом сервера о запрете доступа к файлу.
Пример информирования пользователя, взамен простого отказа:
sub handler {
....
if ( -f ($resource->filename) && (time() - $mtime) < 60 ) {
$resource->send_http_header("text/plain");
$resource->print("Please wait. This file is not ready for download. Пожалуйста подождите. Этот файл ещё не готов к скачиванию.");
return OK;
} else {
....
....
if ( -f ($resource->filename) && (time() - $mtime) < 60 ) {
$resource->send_http_header("text/plain");
$resource->print("Please wait. This file is not ready for download. Пожалуйста подождите. Этот файл ещё не готов к скачиванию.");
return OK;
} else {
....
Перезапускаем Nginx:
# /etc/init.d/nginx restart