排他処理(LOCK)に関して。
ファイルが書き込まれる際多数の人が同時に書き込むとLOGが正しく書き込まれない。
なので、排他処理をする必要がある。
OPEN関数でやるLOCKは機能的に信用できないLOCK形式である。
例)
local($lock);
for (1..10) {
if (-e "$lockfile") {
sleep(1);
} else {
open(LOCK,">$lockfile")||&error('LOCKファイルが開けません','パーミッションを確かめてください');
close(LOCK);
$lock++;
last;
}
}
if(!$lock) {
&error('CGIは多忙です。','ちょっと待ってからやり直してください。');
}
一般に言うKent式の簡易LOCK。
これが信用ならない点について。
ファイルの存在をチェックして、ない場合ファイルを作りLOCKをかける仕組みだが、これではチェックと作成と二つのステップが必要であるため、もしファイルをチェックしたところでプロセスが切り替わりファイルを作る前にもう一つのプロセスがファイルチェックを行った場合、同時に二つのプロセスがLOCKをかける処理を行ってしまうことになる。
(擬似マルチタクスとして、作業を細かく切り替えて処理をしているために起こる)
以上のようなことがないようにするには、チェックと作成を同時に行わなければならない。
しかし、サーバによっては使えないコマンドがあるので、なるべくすべてのサーバにて動作する形式にしなければならない。
たとえば、flockだ。LOCKのための命令で使えれば便利であるのだが、Winサーバでは動かない。
あとはsymlinkが同時に行えるが、これも使えない環境があるため見送る。
今回はmkdirを使ってみる。
実際に行うLOCKは以下のとおり。
local($lock);
undef($lock);
for (1..1000) { # あまり少ないとビジー時間が重なりエラーになってしまう(むやみに排他してしまう)
unless (mkdir(lockdir, 0777)) {
sleep(0.01);
} else {
$lock++;
last;
}
}
if(!$lock) {
&error('CGIは多忙です。','ちょっと待ってからやり直してください。');
}
ロック解除は
rmdir(lockdir);
で行う。
複数プロセスを立ち上げテストした。
テスト方法。
for(1..1000) {
&lock;
open(IN,$file);
$no = <IN>;
close(IN);
$no++;
open(OUT,">$file");
print OUT $no;
close(OUT);
rmdir(lockdir);
}
というようなことをやり、同時に複数のブラウザを立ち上げる。
まともにLOCKがかかれば正しくカウントアップするはずである。
OPENの場合は2から3個のプロセスを立ち上げたところでまともにカウントアップされなかった。
mkdirは数個立ち上げたがまともにカウントをした。
(10個ほど同時起動を試したが、立ち上げに時間がかかり同時起動は数個になってしまった。もっと厳密にやろうと思えばできなくはないが、そこまでやる必要はナイトいう判断から今回は見合わせた)
通常の個人サイトくらいではそれほど重要にはならないと思うが、
同時アクセスが数百や数千ほどある人気サイトでは必須の事項である。
|