UMGUM.COM 

Nginx + Perl ( Отдаём данные зеркалированных ресурсов с помощью Nginx + Perl. )

26 октября 2010  (обновлено 31 января 2015)

OS: Debian Lenny.

И так, сервис зеркалирования работает. Ранее я обратил внимание на возможность получения пользователем "битого" файла, который в момент обращения к нему находится в процессе скачивания 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

Распаковываем архив. Переходим в корень директории с исходными кодами:

# tar -xvf nginx-0.8.53.tar.gz
# 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

Вывод конфигуратора "говорящий" и явно даёт понят, успешно ли прошла процедура.

Компилируем продукт:

# 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

Останавливаем и удаляем пакет работающего ранее Nginx:

# /etc/init.d/nginx stop
# aptitude remove nginx

Устанавливаем подготовленный нами пакет Nginx+Perl и запускаем сервер:

# dpkg -i ./nginx-perl_0.8.53-1_i386.deb
# /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

Теперь дополним файлы конфигурации 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;
....
    }
}

Пишем модуль с процедурой проверки ресурса:

# 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;

Как видно, "отлов" загружаемого файла производится на основе данных времени модификации файла. 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 {
....

Перезапускаем Nginx:

# /etc/init.d/nginx restart


Заметки и комментарии к публикации:


Оставьте свой комментарий ( выразите мнение относительно публикации, поделитесь дополнительными сведениями или укажите на ошибку )