在mod_perl2下使用Apache :: DBI的安全方法是什么?

时间:2010-03-31 09:17:03

标签: perl fork dbi mod-perl2 apache-dbi

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

  

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

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

  

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

     

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

这是原始代码:

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进行分叉?有没有办法让它创建一个新的连接呢?

2 个答案:

答案 0 :(得分:1)

我看到了几个选项:

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

e.g:

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的内容以来已经有一段时间了;它可能会为你解决这个问题。)

答案 1 :(得分:0)

不要在mod_perl2下分叉。使用Apache2::Subprocess。另请参阅Is it a bad idea to fork under mod_perl2?