[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