« DBI::Printf - A Yet Another Prepared Statement | メイン | DBIx::Printf と LIKE 式 »
2007年10月02日
Perl で並列処理 (using マルチプロセス)
Shibuya.pm で牧さんが Gungho の話をされたそうで、スライドを拝見しました。Pathtraq への言及もあってうれしい。
で、スライドを読んでいて、HTTP アクセスと HTML 解析/保存処理を分離すべき、となっている点が気になりました。実は Pathtraq (のうち奥が書いている部分) では、Gungho と HTML の解析/保存処理を分離することはしていません。そのかわり、複数のワーカープロセスを駆動することで、スループットを確保しています。分離モデルとマルチプロセスモデル、どちらの手法を採るべきかは、解析/保存処理の重さやエラー処理の設計方針にもよると思うのですが、少なくとも1台のサーバで処理できる間はマルチプロセスモデルで問題ないのではないかと漠然と考えています。注。別に分離したからといって悪いことはないのですが、マルチプロセスモデルには、OS側で自動的に処理能力の配分が行われるので、面倒な非同期処理を書いたり、細かな負荷配分の調整を行わなくても良い、というメリットがあります。
ちなみに、Perl で複数個のワーカープロセスを動かして処理を行う場合のコードは、以下のような感じになるでしょう。Apache の prefork モデルを単純化した形と言うこともできます。デーモン (特にサーバ) を書く場合には、頻出のコーディングパターンかな、と思います。
#! /usr/bin/perl
use strict;
use warnings;
use Proc::Wait3;
my %worker_pids;
my $num_workers = 4; # ワーカープロセスの数
my $exit_loop;
# サーバの初期化処理
...
# SIGTERM ハンドラをセット
$SIG{TERM} = sub { $exit_loop = 1 };
# メインループ ($num_workers 個のワーカープロセスを駆動)
while (! $exit_loop) {
my $pid;
if (keys %worker_pids < $num_workers) {
$pid = fork;
die 'fork error' unless defined $pid;
if ($pid) {
$worker_pids{$pid} = 1;
} else {
exit(child());
}
sleep 1;
}
if (my ($exit_pid) = wait3(! $pid)) {
delete $worker_pids{$exit_pid};
}
}
# ワーカーを停止して終了
foreach my $c (keys %worker_pids) {
kill 'TERM', $c;
}
while (%worker_pids) {
if (my $exit_pid = wait) {
delete $worker_pids{$exit_pid};
}
}
exit 0;
# ワーカープロセス
sub child {
# SIGTERM ハンドラをセット
$SIG{TERM} = sub { exit 0 };
# 実際の処理
...
# 終了コードを返す
0;
}
ちなみに、わざわざ Proc::Wait3 を使っているのは、perl の wait や waitpid だとシグナルを受け取っても制御が返ってこないためです。今回改めて調べてみて、この事実にたどり着きました。なんともバッドノウハウです。
注1: 1台のサーバで動かす間は、どのみち全体のワークセットをオンメモリに保つことになるので、タスクを分離するメリットがない投稿者 kazuho : 2007年10月02日 19:28
トラックバック
このエントリーのトラックバックURL:
https://labs.cybozu.co.jp/cgi-bin/mt-admin/mt-tbp.cgi/1567
このリストは、次のエントリーを参照しています: Perl で並列処理 (using マルチプロセス):
» スライドの真意 from D-6 [相変わらず根無し]
https://labs.cybozu.co.jp/blog/kazuho/arc... [続きを読む]
トラックバック時刻: 2007年10月02日 21:06