Использование mod_perl
Автор: Бурмистров Андрей
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
- Описание Perl модулей, поставляемые с mod_perl
- 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…
|