Mod_perl — это С модуль Apache, реализующий Perl интерпретатор + набор Perl
модулей, предоставляющих следующие интересные возможности:
- 1. Кэширование откомпилированых cgi скриптов (Apache::Registry.pm)
- 2. Perl интерфейс к C API Apache
Большинство разработчиков
используют mod_perl чтобы увеличить производительность своих скриптов — Apache
не запускает новый процесс при каждом запросе к script.pl, т.к. имеет теперь
свой Perl интерпретатор, и не компилирует script.pl при каждом запросе, т.к.
Apache::Registry.pm хранит ваши откомпилированые скрипты в кэше. Perl интерфейс
к C API Apache используется разработчиками не так часто.
Как это выглядит — типичное использование mod_perl.
Мы скомпилировали
Apache с поддержкой mod_perl, у нас есть script.pl:
#!/usr/bin/perl
use strict;
use CGI qw(:cgi);
print header (), ’Hi, people!’;
и мы хотим, чтобы скрипт выполнялся интерпретатором mod_perl
и кэшировался. Для этого мы изменяем httpd.conf:
# Это мы закомментируем
#### ScriptAlias /cgi-bin/ «/home/my-project.ru/cgi-bin/»
# А это добавим
PerlModule Apache::Registry
<Location /cgi-bin>SetHandler perl-script PerlHandler Apache::Registry
Options ExecCGI
allow from all
</Location>
Перезапустим Apache и убедимся, что всё работает.
Конфигурирование mod_perl.
Разберёмся с тем, что мы написали
в httpd.conf.
Mod_perl определяет
Apache handler с именем
"perl-script". Следующая запись:
<Location /cgi-bin>SetHandler perl-script</Location>
означает, что запросы к /cgi-bin будет обрабатывать код
mod_perl.
Mod_perl определяет также несколько директив:
PerlModule Some::Module Other::Module
— директива загружает указаные
Perl модули. Скрипты, выполняемые под mod_perl, могут их использовать, не
включая функцией use(). С точки зрения скрипта, использование PerlModule
отличается от use() тем, что в скрипт не импортируются имена модуля. Модуль,
загруженый PerlModule, может также использоваться как обработчик
какой-либо фазы обработки запроса, см. ниже.
Perl*Handler Some::Module
— Apache обрабатывает запрос в несколько
фаз — (Post-Read-Request, URI Translation, Header Parsing, Access
Control, Authentication, Authorization, MIME type checking, FixUp, Response(!),
Logging, Cleanup). Директива Perl*Handler регестрирует
модуль-обработчик для какой-либо из этих фаз. Наиболее
часто используется директива PerlHandler, устанавливающая обработчик для фазы
Response. Остальные Perl*Handler директивы (PerlPostReadRequestHandler,
PerlTransHandler, PerlHeaderParserHandler, PerlAccessHandler, PerlAuthenHandler,
…) используются реже.
В нашем httpd.conf мы указали Apache::Registry.pm
как обработчик фазы Response. Когда мы запрашиваем script.pl у сервера,
Apache::Registry.pm берёт откомпилированую версию скрипта из своего кэша
и запускает. Apache::Registry.pm отслеживает изменения script.pl на диске и при
необходимости перекомпилирует его. Однако изменения в модулях, включаемых
script.pl не отслеживаются — это долго. Так что если вы изменили
какой-либо из своих модулей, перезапустите сервер.
Вы,
разумеется, не обязаны использовать Apache::Registry.pm, как делают большинство
разработчиков. Напишите ради интереса свой обработчик фазы Response:
package MyOwnResponseGenerator;
use strict;
use Apache::Constants qw(:common);
sub handler {
print «Content-type: text/html\n\n»;
print «Hi people! enjoy this response!»;
return OK; # We must return a status to mod_perl
}
1;
MyOwnResponseGenerator.pm положим там, где мod_perl сможет его найти (perl
-e ’print join "\n"e;, @INC’). Отредактируем httpd.conf:
PerlModule MyOwnResponseGenerator
<Location /somelocation>
SetHandler perl-script PerlHandler MyOwnResponseGenerator
PerlSendHeader On
</Location>
Перезапустим Apache, наберём
http://my-project.ru/somelocation.Работает!
Подробно
о конфигурировании mod_perl:
http://perl.apache.org/docs/1.0/guide/config.html
Особенности работы скриптов под Apache::Registry
Две главные особенности
работы кэшируемых скриптов, способные испортить много нервов разработчику, если
он о них не знает:
1. Глобальные переменные скрипта сохраняют свои
значения между запросами:
#!/usr/bin/perl
use strict;
use CGI qw(:cgi);
use vars qw($i);
print header (), ++$i;
запросим этот скрипт несколько раз подряд и получим:
1 2 3 4 5 …без Apache::Registry картина была бы такой,
разумеется:
1 1 1 1 1 …Не удивляйтесь, если
открыв новое окошко IE и повторив процедуру вы снова получите:
1 2 3 4 5 … вместо
6 7 8 9 10 …
. Как правило на сервере работают несколько дочерних процессов httpd, каждый из
них имеет свою копию вашего скрипта и соответственно свою копию $i.
Команда
httpd -x запустит сервер с одним дочерним процессом —
удобно для отладки.
Ниже следует пример того, как писать НЕ НАДО:
#!/usr/bin/perl
use CGI qw(:cgi);
use MySecurity qw(:check_user);
use vars qw($allow);
$allow = 1 if check_user (param(’login’), param(’password’));
print header ();
print (($allow) ? ’Some secret data’ : ’Cool hacker? Go away!’);
достаточно одному пользователю указать верные login-password,
и все остальные смогут заходить просто так :).
2. Определённые
в вашем скрипте функции становятся вложенными во внешнюю функцию. Вот во что
Apache::Registry превращает script1.pl:
package Apache::ROOT::perl::script1_2epl;
use Apache qw(exit);
sub handler {
BEGIN { $^W = 1; };
$^W = 1;
### Original begin of script1.pl
use strict;
use CGI qw(:cgi);
my $var = ’a’;
sub show_var { "Var was $var and now is " . ++$var }
print header(), show_var ();
### Original end of script1.pl
}
наша
show_var() оказалась определённой внутри
handler(), т.е. вложеной. Чем нам это грозит?
show_var() не может теперь корректно работать с внешними
my() переменными, в нашем случае с
my $var. Запустив
скрипт несколько раз получим:
Var was a and now is b,
Var was b and now is c,
Var was c
and now is d и т.д.
Функция
show_var()
’видит’ тот экземпляр
my $var, который она ’видела’
при первом выполнении
handler().
Подробно эта
проблема описана здесь:
Scoped
Variable in NestedSubroutines. Нас же интересуют её возможные
решения:
1. определять функции не в скрипте, а в модуле. Код ваших
модулей не обарачивается ни в какие функции, и данная проблема не возникнет.
2. или просто не обращатся из функций к внешним my()
переменным.
Предположим, вы пересадили свои скрипты на mod_perl, они
некорректно работают из кэша из-за описаных выше проблем, и вам
лень их адаптировать. Используйте Apache::PerlRun вместо Apache::Registry.
Скрипты не будут кэшироваться, однако по прежнему будут выполняться встроеным
Perl интерпретатором.
Подробно о работе скриптов под mod_perl:
http://perl.apache.org/docs/1.0/guide/porting.html
Производительность
Оценим ради интереса выигрыш в производительности,
который даёт mod_perl:
1.
time lwp-request http://my-project.ru/cgi-bin/script.pl —запустим несколько
раз и оценим среднее время выполнения lwp-request. Наш script.pl
здесь работает под Apache::Registry.
2.
time
lwp-request
http://my-project.ru/cgi-bin/script.pl — сделаем то же
самое, но script.pl работает без mod_perl.
3.
time
lwp-request
http://my-project.ru/little_page.html — так мы оценим время,
которое в предидущих двух случаях тратилось на компиляцию
lwp-request(ибо он на Perl), на передачу запроса и ответа по сети
и т.д. вообщем на всё, не касающееся script.pl.
На моём 300Celeron
результаты следующие: 774, 1039, 768 миллисекунд соответственно. Т.е. под
mod_perl скрипт выполнился за 6мс (774–768), без mod_perl — за 71мс (1039–768).
В 12 раз возросла прозводительность, однако! Для реальных скриптов эта цифра
будет меньше, конечно.
Пора закругляться
- Где взять
- http://perl.apache.org/dist/mod_perl-1.0-current.tar.gz
- http://perl.apache.org/docs/1.0/api/index.html
- Документация
- http://perl.apache.org/docs/1.0/index.html
- Кто использует
- http://slashdot.org/
- http://search.cpan.org/
- http://www.wired.com/
- More…