【问题标题】:What is the safe way to use fork with Apache::DBI under mod_perl2?在 mod_perl2 下将 fork 与 Apache::DBI 一起使用的安全方法是什么?
【发布时间】:2011-02-02 20:19:46
【问题描述】:

我在子进程中使用 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 和分叉?有没有办法让它创建一个新的连接?

【问题讨论】:

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


    【解决方案1】:

    我看到了几个选项:

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

    例如:

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

    【讨论】:

    • @codeholic:你说它不起作用是什么意思?您必须再次调用 $dbh = DBI->connect(...),因为 $dbh 现在未定义。
    • @codeholic:好的,我认为我的回答中的所有内容都是红鲱鱼 - 你似乎有其他事情发生。即“某事”不正确地传递了 AutoCommit 属性。这个问题是刚刚开始的新问题,还是你一直都有?我怀疑您的配置中发生了一些奇怪的事情。
    • 它仅在启用Apache::DBI 的情况下被复制。我的配置没有什么奇怪的,只是简单的PerlModule Apache::DBI。当我将其注释掉并返回到普通的DBI 时,问题就消失了(鉴于我重新连接了孩子)。不过好吧,明天我会尝试在其他配置不同的服务器上重现此错误。
    • 但是,这个问题似乎已经过时了,因为我将改用Apache::SubProcess,如friedo所述:stackoverflow.com/questions/2554827/…
    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 2011-02-03
      • 2012-12-25
      • 1970-01-01
      • 2017-04-12
      • 2018-04-10
      • 2013-01-02
      • 1970-01-01
      • 2020-08-27
      • 2021-02-04
      相关资源
      最近更新 更多