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;
}

fork で Apache::DBI を安全に使用する方法はありますか? おそらく新しい接続を作成する方法はありますか?

4

2 に答える 2

1

いくつかのオプションが表示されます。

  • フォークするときに DB ハンドルを明示的に閉じ、必要に応じて再度開きます。

例えば:

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の下でフォークするのは悪い考えですか?も参照してください。

于 2010-04-27T00:39:40.110 に答える