Как мне создать постоянный дескриптор соединения (для MySQL и memcached) в mod_perl2 для каждого дочернего процесса Apache2?
Я в конце своего остроумия. Моя текущая (неудачная) реализация небольшой веб-страницы, поддерживаемой MySQL, в значительной степени опирается на модуль Project:: Connection, в котором хранятся два дескриптора области пакета (our'd) (mysql_handle и memc_handle), которые инициализируются с помощью вызова Project::Connection::child_init на этапе PerlChildInitHandler. Точно так же я (хочу) отключить соединения во время фазы PerlChildExitHandler. Проблема в том, что моя отладка подтвердила, что обработчики правильно созданы во время подпрограммы ChildInit, но когда я запрашиваю "GET /", модуль, который обрабатывает запрос (Project::Web::Home, который использует Project::Connection), сообщает что обе ручки не определены!
Вот соответствующий код:
startup.pl (вызывается с помощью PerlRequire в конфигурации сервера):
use Apache2::Reload; # PerlInitHandler inside /var/www
use Apache2::ServerUtil;
use lib "/var/www/app";
use Project; # has the response handler
use Project::Connection; # has the childinit and childexit handlers
$s = Apache2::ServerUtil->server;
$s->set_handlers('PerlChildInitHandler' => \&Project::Connection::child_init);
$s->set_handlers('PerlChildExitHandler' => \&Project::Connection::child_exit);
1;
Project / Connection.pm (содержит дескрипторы соединения):
package Project::Connection;
use DBI;
use Cache::Memcached;
use Project::Config;
our ($mysql_handle, $memc_handle);
sub mysql { $mysql_handle }
sub memc { $memc_handle }
sub child_init {
# create the mysql connection
my ($db, $host, $port, $user, $pass)
= Project::Config::get('db', 'db_host', 'db_port', 'db_user', 'db_pass');
$mysql_handle = DBI->connect("dbi:mysql:database=$db;host=$host;port=$port",
$user, $pass)
or die "Failed to establish a connection with mysqld: $DBI::errstr";
# create the memcache connection
my ($socket) = Project::Config::get('memcached_sock');
$memc_handle = Cache::Memcached->new('servers' => $socket)
or die "Failed to establish a connection with memcached";
}
sub child_exit {
$mysql_handle->disconnect;
$memc_handle->disconnect_all;
}
1;
Project.pm:
package Project;
use Apache2::Const;
use Apache2::Request;
use Project::Web::Home;
sub handler {
my ($request_rec) = @_;
my $request = Apache2::Request->new( $request_rec );
# load the module for the webpage
my $page = substr $request->uri, 1;
$page = length $page ? 'home' : $page; # take care of directory requests
eval "Project::Web::${page}::do";
if ($@) {
Project::Web::home::do; # do this rather than 404
}
Apache2::Const::OK;
}
1;
Проект /Web/home.pm:
package Project::Web::home;
use strict;
use warnings;
use Project::Connection;
sub do {
my ($user, $args) = @_;
print "Content-Type: text/plain\n\n";
$dbh = Project::Connection::mysql; # this is undefined!
# ...
}
1;
Теперь я понимаю, что во время запуска Apache, когда он компилирует и запускает startup.pl, все модули компилируются в родительский процесс. Затем родительский процесс разветвляется на дочерние процессы, копируя скомпилированный код, так что дочерний процесс становится копией родительского. После этого дочернему процессу СЛЕДУЕТ запустить Project::Connection::child_init, который инициализирует дескрипторы для mysql и memcache, так что когда дочерний элемент обрабатывает "GET /", модуль Project::Web::home может "использовать Project::Connection" "чтобы получить эти ручки; и, наконец, эти маркеры должны оставаться определенными для всего процесса ребенка.
Разделяя дополнительную информацию, вы можете захотеть узнать:
Из журнала: "Apache/2.2.16 (Debian) mod_apreq2-20090110/2.7.1 mod_perl/2.0.4 Perl/v5.10.1 настроен" Из /etc/apache2/sites-available/project:
<VirtualHost *:80>
ServerAdmin admin@project.com
ServerName project.com
DocumentRoot "/var/www"
PerlRequire "/var/www/bin/startup.pl"
<Directory "/var/www/">
SetHandler "perl-script"
PerlInitHandler "Apache2::Reload"
PerlResponseHandler "Project"
PerlOptions -SetupEnv +ParseHeaders
Options ExecCGI
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
1 ответ
Этот дент работает и для меня. Я использовал небольшой вариант здесь. Я пытался создать соединение в обработчике всякий раз, когда объект подключения ($mysql_handle
) был пуст. Это привело к созданию связи во время первого запроса, и она кэшировалась на протяжении всей жизни ребенка.
Я не мог понять, почему объект, инициализированный во время дочернего init, становится неопределенным во время фазы ответа, но эта работа, казалось, работала для меня.
Короче, моя работа была похожа на
sub mysql { &child_init unless defined $mysql_handle; return $mysql_handle }
Это создало и кэшировало одну связь, как и ожидалось на протяжении всей жизни ребенка.