0

我在子进程中使用 Apache::DBI 时遇到问题。问题是 Apache::DBI 为所有使用它的进程提供了一个句柄,所以我得到

DBD::mysql::db selectall_arrayref 失败:命令不同步;您现在无法在 /usr/local/www/apache22/data/test-fork.cgi 第 20 行运行此命令。

重新连接没有帮助,因为 Apache::DBI 在所有进程中重新连接,因为我理解以下错误

服务器遇到内部错误,无法完成您的请求。

错误消息:DBD 驱动程序尚未在 /usr/local/lib/perl5/site_perl/5.8.9/Apache/DBI.pm 第 283 行实现 AutoCommit 属性。,

这是原始代码:

use Data::Dumper 'Dumper';
use DBI ();

my $dbh = DBI->connect($dsn, $username, $password, {
        RaiseError => 1,
        PrintError => 0,
    });
my $file = "/tmp/test-fork.tmp";

my $pid = fork;
defined $pid or die "fork: $!";

if ($pid) {
    my $rows = eval { $dbh->selectall_arrayref('SELECT SLEEP(1)') };

    print "Content-Type: text/plain\n\n";
    print $rows ? "parent: " . Dumper($rows) : $@;
}
else {
    my $rows = eval { $dbh->selectall_arrayref('SELECT SLEEP(1)') };

    open FH, '>', $file or die "$file: $!";
    print FH $rows ? "child: " . Dumper($rows) : $@;
    close FH;
}

我用于重新连接的代码:

...
else {
    $dbh->disconnect;
    $dbh = DBI->connect($dsn, $username, $password, $attrs);
    my $rows = eval { $dbh->selectall_arrayref('SELECT SLEEP(1)') };

    open FH, '>', $file or die "$file: $!";
    print FH $rows ? "child: " . Dumper($rows) : $@;
    close FH;
}

有没有一种安全的方法来使用 Apache::DBI 和分叉?有没有办法让它创建一个新的连接?

4

2 回答 2

1

我看到几个选项:

  • 分叉时显式关闭数据库句柄,并根据需要重新打开它们。

例如:

my $dbh = DBI->connect(...);

my $pid = fork;
defined $pid or die "fork: $!";

if ($pid) {
    # parent...
}
else {
    # child...
    undef $dbh;

这可以通过将 存储$dbh在一个对象中并根据需要将该对象传递给系统的某些部分来简化。该对象将负责根据需要重新打开 $dbh,因此应用程序的其余部分不必关心细节。保持代码封装并与系统的其他部分良好分离。

我在系统中的 Moose 对象内使用 DBIx::Connector,该对象使用方法委托来提供 dbh。该应用程序只是执行以下操作:

my $dbh = $db_dbj->dbh;
my $sth = $dbh->prepare(...);
# more boring DBI code here

...并且 dbh 会根据需要重新连接/重新生成,不可见。


顺便说一句,在多进程环境中使用裸文件句柄时应该非常小心。您可能很容易破坏您的数据。open (my $fh, $file) or die "Cannot open $file: $!"安全得多。

看到你使用eval {}块而不检查$@. 您只是在掩盖错误,而不是处理错误,因此发生的事情可能比您意识到的要多。检查您的结果值(或者更好的是,使用显式异常处理模块,例如Try::Tiny。使用use strict; use warnings;

PS。我刚刚注意到您明确地包含DBI在您的代码中。不要那样做。如果你在你的 startup_modperl.pl(或任何你称之为引导文件的文件)中使用 Apache::DBI,你永远不必包含 DBI 本身。我不能肯定地说,但我不相信正确的包被调用(自从我查看 Apache::DBI 的胆量以来已经有一段时间了;不过它可能会为你解决这个问题)。

于 2010-03-31T14:55:32.163 回答
0

不要在 mod_perl2 下分叉。使用Apache2::Subprocess。另请参阅在 mod_perl2 下 fork 是不是一个坏主意?

于 2010-04-27T00:39:40.110 回答