Что такое Apache::Registry
Apache::Registry - это модуль Apache, который позволяет сохранять скомпилированный mod_perl код в памяти для дальнейшего использования.
Дочерний процесс Apache, компилирует код один раз и сохраняет в памяти, где он хранится до его перезагрузки (если скрипт был изменен) или завершения работы процесса. Каждый процесс использует СВОЮ копию скрипта.
При дальнейшем обращении к скрипту, он извлекается из кеша и сразу выполняется. Apache::Registry сохраняет в памяти время последней модификации perl-скрипта. Если исходный код был обновлен, скрипт будет скомпилирован заново.
Настройка Apache::Registry в httpd.conf :
|
1 2 3 4 5 6 |
< Directory /home/aninatalie/aninatalie.ru/cgi> SetHandler perl-script PerlHandler Apache::Registry Options ExecCGI ... < /Directory> |
Необходимо отметить, что perl-скрипт будет скомпилирован как подпрограмма, входящая в состав другой, внешней, подпрограммы, и получившийся код будет сильно отличаться от исходного. Данную особенность необходимо учитывать при написании кода, который будет работать под управлением Apache::Registry. Подпрограммы, изменение которых не желательно, следует помещать в модули. Код модулей при их компиляции и выполнении не оборачивается во внешнюю подпрограмму.
Еще одна особенность работы с Apache::Registry: глобальные переменные скрипта сохраняют свои значения между запросами.
Классический пример того, как будет выглядеть исходный код скрипта после обработки Apache::Registry.
Исходный скрипт (/cgi/test.pl):
|
1 2 |
print "Content-type: text/html\n\n"; print "It works\n"; |
Что в реальности будет запускаться на выполнение:
|
1 2 3 4 5 6 7 |
package Apache::ROOT::cgi::test_e2pl; use Apache qw(exit); sub handler { print "Content-type: text/html\n\n"; print "It works\n"; } |
Исходный perl-скрипт на выходе выполняется как самостоятельный модуль. Стоит обратить внимание на название пакета: Apache::ROOT::cgi::test_e2pl.
Каждый дочерний процесс Apache сохраняет для себя в памяти "готовые к употреблению" скрипты. Один и тот же скрипт может быть помещен в память несколько раз - для каждого процесса отдельно. Во избежание конфликта имен, Apache::Registry назначает каждому пакету уникальное название, с помощью добавления уникального ключа к имени пакета.
Особенности разработки под Apache::Registry
Использование exit()
Стандартная функция Perl - exit() / CORE::exit() - не может использоваться в разработке под mod_perl. Функция приводит к завершению процесса mod_perl. При этом, будут пропущены этапы логирования и корректного завершения работы процесса. Например, не оборвется соединение с базой данных, созданные хендлеры останутся в памяти. Минимальные возможные последствия: утечки памяти.
Apache::Registry и Apache::PerlRun негласно замещают стандартную функцию exit() функцией Apache::exit(). Поэтому, код с exit(), который работает под управлением данных модулей, можно использовать без изменений.
Если необходимо принудительно завершить дочерний процесс - рекомендуется использовать
Apache::exit (Apache::Constants::DONE). Apache::exit позволит корректно завершить процесс, произведя необходимые записи в логи и соблюдая требования протокола http.
Если необходимо завершить дочерний процесс после того, как завершено выполнение запроса - можно
использовать $r-> child_terminate . Данный метод устанавливает значение для переменной MaxRequestsPerChild равным 1 и присваивает флагу keepalive значение false. После завершения выполнения запроса, родительский процесс инициирует завершение дочернего процесса, если значение MaxRequestsPerChild меньше или равно числу выполненных запросов.
Использование die()
die() / CORE::die() - обычно используется для прерывания выполнения программы в случаях, когда что-то идет не так, как ожидалось.
|
1 |
my $dbh = DBI->connect( @{ $DB_CONNECT_PARAMS },{RaiseError=>0}) or die $DBI::errstr; |
При срабатывании die(), выполнение кода прерывается, печатается причина прерывания и завершается работа интерпретатора Perl.
При работе с mod_perl, завершение работы процесса не желательно. При вызове die(), mod_perl делает запись о возникшей ошибке в лог, и вызывает Apache::exit() вместо CORE::die().
Использование STDIN, STDOUT и STDERR
Под mod_perl, STDIN и STDOUT привязаны к сокету, из которого принят запрос.
STDERR привязан к файлу, определенному директивой ErrorLog.
Указанные положения исключают возможные конфликты доступа и разделения STDIN, STDOUT и STDERR.
Использование print()
Под mod_perl, CORE::print() автоматически переадресует переданные ему аргументы Apache::print().
В контексте mod_perl, нижеприведенные надписи эквивалентны:
|
1 2 |
print "Hello"; $r->print("Hello"); |
Apache::print() прерывает выполнение и ничего не выводит на печать, если $r->connection->aborted возвращает true. Это происходит в случае прерывания связи клиентом (например, закрытием браузера).
Использование __END__ и __DATA__
Скрипты под Apache::Registry не могут содержать лексемы __END__ и __DATA__ , потому что, во время обработки, исходный код скрипта помещается в код подпрограммы handler().
Если скрипт содержит __END__ или __DATA__ :
|
1 2 3 4 |
print "Content-type: text/plain\n\n"; print "Hi"; _ _END_ _ Some text that wouldn't be normally executed |
то, после обработки и перед запуском, выполняемый код будет иметь вид:
|
1 2 3 4 5 6 7 8 |
package Apache::ROOT::cgi::test_2epl; use Apache qw(exit); sub handler { print "Content-type: text/plain\n\n"; print "Hi"; _ _END_ _ Some text that wouldn't be normally executed } |
Поскольку Perl игнорирует все, что идет за лексемой __END__ - программа будет выдавать ошибку.
error.log:
|
1 2 |
[error] Missing right curly or square bracket at /cgi/test.pl line 58, at end of line\nsyntax error at /cgi/test.pl line 58, at EOF\n |
Переменные окружения $ENV {SERVER_NAME}, $ENV {REMOTE_USER} и др.
Apache::Registry эмулирует переменные окружения mod_cgi - $ENV {SERVER_NAME}, $ENV {REMOTE_USER} и др.
Отключить данную функциональность можно с помощью конфигурационной директивы PerlSetupEnv Off
Использование массива @INC
Значение массива @INC при работе с mod_perl, можно изменить только один раз - при запуске сервера. После выполнения каждого запроса к серверу, mod_perl возвращает @INC первоначальное значение.
Если mod_perl встретит в скрипте директиву
|
1 |
use lib qw(foo/bar); |
он внесет изменения в @INC, но действовать они будут только до завершения периода компиляции. После этого @INC примет исходное значение.
Есть два способа изменить @INC при запуске сервера:
- использовать в конфигурационном файле сервера директивы
1PerlSetEnv PERL5LIB /home/httpd/perl
или
1PerlSetEnv PERL5LIB /home/httpd/perl:/home/httpd/mymodules - прописать в startup.pl файле команду:
12use lib qw(/home/httpd/perl /home/httpd/mymodules);1;
и внести изменения в конфигурационный файл сервера, добавив:
1PerlRequire /path/to/startup.pl
Перезагрузка модулей
По-умолчанию, сервер не отслеживает факт модификации модулей, подключенных через use() или require() и считает, будто никаких изменений не производилось. При этом, использует ранее сохраненный в памяти вариант модуля.
На наличие изменений проверяются только perl-скрипты.
Есть несколько способов заставить сервер с mod_perl отслеживать изменения и, при необходимости, перезагружать модули.
Перезагрузка сервера
Простейший вариант - просто перезагружать сервер всякий раз, когда вы вносите изменения в Ваш код.
Использование Apache::StatINC
После подключения файла, с помощью require(), в глобальный хэш %INC вносится новая запись. Ключом записи является имя подключенного файла, значением - путь к нему. Apache::StatINC просматривает %INC и немедленно перезагружает все измененный модули.
Подключить Apache::StatINC можно с помощью добавления директив в конфигурационный файл сервера - httpd.conf:
|
1 2 |
PerlModule Apache::StatINC PerlInitHandler Apache::StatINC |
Примечание:
Если подключается модуль, расположенный в той же директории, что и исходный скрипт, но текущая директория не указана в массиве @INC, в %INC будет внесена запись типа:
|
1 |
'MyModule.pm' => 'MyModule.pm' |
Когда Apache::StatINC попытается найти указанный модуль - произойдет ошибка, поскольку он ищет только в директориях, которые указаны в @INC. Решение: заранее указывать все необходимые директории в @INC .
Полезные ссылки
Stas Bekman. Practical mod_perl /By Stas Bekman, Eric Cholet - O'Reilly, 2003. - 924 с.
Writing Apache Modules with Perl and C
What is mod_perl
Использование mod_perl. Бурмистров Андрей