[ANN] nginx-perl

Alexandr Gomoliako zzz на zzz.org.ua
Вт Ноя 1 17:48:42 UTC 2011


Hello,

Предлагаю попробовать расширенный встроенный перл.
Я решил переделать все то, что мне не нравилось в прошлом
моем модуле, но уже в виде патча для nginx,

Взять в виде дистрибутива можно тут:
    https://github.com/zzzcpan/nginx-perl

Вся документация пока только в этом письме.

Старый API не меняется и практически не тронут:
    http://nginx.org/ru/docs/http/ngx_http_perl_module.html


1. Установка

Сразу советую собрать с новым перлом, использованиe
памяти в других не обрадует:
Сейчас это довольно просто:
    % sudo cpan App::perlbrew
    % perlbrew --force install perl-5.14.2

И через пол часа получите перл в
    /home/<user>/perl5/perlbrew/perls/perl-5.14.2/

Теперь nginx, перл там включен сразу, нужно только
указать, с каким собрать:
    % ./configure
--with-perl=/home/<user>/perl5/perlbrew/perls/perl-5.14.2/bin/perl
    % make

В дистрибутиве есть готовый конфиг для быстрого старта:
    % mkdir hello/logs
    % ./objs/nginx -p hello

Пример модуля лежит в hello/lib/Hello.pm, конфиг в hello/conf/nginx.conf.


2. Асинхронные ответы

Можно отвечать на запросы асинхронно из другого
обработчика:

    sub handler {
        my $r = shift;

        $r->main_count_inc;

        # ... здесь создаем обработчики для других событий

        return NGX_DONE;
    }

NGX_DONE позволяет не обрабатывать запрос по старому,
а сразу выйти из обрабтчика и вернуться в event loop.

Чтобы отправить ответ позже, нужно две строчки:

    $r->send_special(NGX_HTTP_LAST);
    $r->finalize_request(NGX_OK);

Пример, создать таймер и ответить через секунду:

    sub handler {
        my $r = shift;
        $r->main_count_inc;

        ngx_timer 1, 0, sub {
            $r->send_http_header("text/html");
            $r->print("Hello\n");

            $r->send_special(NGX_HTTP_LAST);
            $r->finalize_request(NGX_OK);
        }

        return NGX_DONE;
    }


3. Асинхронные соединения:

  - все ошибки устанавливаются в $!,
  - EOF считается ошибкой, можно проверить условием:
        $! == NGX_EOF
  - flow control происходит через return:
        return NGX_READ;
        return NGX_WRITE;
        return NGX_CLOSE;

Чтобы подключиться к какому-то хосту используется
ngx_connector:

    ngx_connector $host, $port, $timeout, sub {
        return NGX_CLOSE  if $!;
        my $connection = shift;
        ...
    };

Внутри коннектора можно создавать функции чтения
и записи:

    ngx_writer $buf, $timeout, sub {
        return NGX_CLOSE  if $!;
        ...
        return NGX_READ;
    };

    ngx_reader $buf, $min, $max, $timeout, sub {
        return NGX_CLOSE  if $!;
        ...
        return NGX_CLOSE;
    };

ngx_reader и ngx_writer можно пересоздавать
внутри сколько угодно раз.

Пример: подключиться к какому-то фтп и считать
приветствие:

    ngx_connector '87.51.34.132', 21, 5, sub {
        return NGX_CLOSE  if $!;

        my $c = shift;
        my $buf = '';

        ngx_reader $c, $buf, 0, 0, 5, sub {
            return NGX_CLOSE  if $!;
            return NGX_READ  if $buf !~ /\x0a/;

            warn "buf = $buf\n";

            return NGX_CLOSE;
        };

        return NGX_READ;
    };

Более сложный пример можно найти в hello/lib/Hello.pm,
функция selftest_get. Она отправляет простой http запрос,
принимает ответ и возвращает результат в колбэк.


4. Подход

Уже наверное понятно, какой подход реализован:
  - все, что вложено --  последовательно
  - все, что подряд -- параллельно

Все переменные хранятся в пэдах, никаких объектов.
Чтобы не запутаться, лучше так и продолжать, это довольно
просто:

    sub foo ($\$&) {
        my ($arg, $arg_ref, $cb) = @_;
        # ...  здесь можно вызывать таймер, коннектор и т.д.
        &$cb();
    }

Вызов:
    foo 1, $buf, sub {
         print $buf;
    };


5. Баги

Модуль довольно большой, так что не без багов.

Будьте осторожны с отправкой ответа два раза, это
вызовет segfault, потому что $r уже после первого будет
указывать на осоводившуюся память.
Со временем пофиксим.


На всякий случай прикладываю и в виде патча.


Подробная информация о списке рассылки nginx-ru