WEB相談室

Webページ作成に関しての何でも掲示板です。

タイトル:同じCGIを連続実行できないか

0:[投稿] ゆいちゃん [MAIL] [2002/05/17 13:34 ][環境:WIN+IE WIN系+Perl]

現在、ファイルを更新するCGIを作成しています。
このCGIは同じCGIで作成されたフォーム内にある更新ボタンによってコールされます。
処理の流れは、
  ロックファイル作成
  ファイルを更新
  ロックファイル削除
です。
問題は、ファイルを更新している最中にもう一度、更新ボタン(ブラウザの戻るでも同じ)
をクリックされてしまうと
前の処理は強制終了され、新たに処理が始まるということです。
これによりロックファイルだけが作成されてしまい以後処理ができないようになります。
中田昭雄氏に尋ねてみたところ、AN HTTPD の仕様であるそうで対応を考えてくれるそう
です(http://homepage1.nifty.com/yito/namazu/gbook/20020516.1528.html を参照)
がとりあえず対応策として今までみなさんはどのようにしていたのか知りたいのです。
方法やサンプルなどがあれば教えてください。
自分ではどうしたらよいのかわからなくて・・・
よろしくお願いします。

環境
 OS    :Windows XP,ME
 httpサーバ:AN HTTPD(中田昭雄氏作成)
 言語   :ActivePerl 5.61


1:[回答] ナン [2002/05/20 11:25 ]

>これによりロックファイルだけが作成されてしまい以後処理ができないようになります。

でしたら、出来なくなった時に、長めにスリープさせて、ロックファイルを削除するとか。

ロックにこだわってロジックを複雑にするより、ファイルバックアップ処理を充実させるとか。


2:[回答] jam [2002/05/20 13:52 ]

シグナルをキャッチしてロックファイルを殺すとか。


3:[質問] ゆいちゃん [MAIL] [2002/05/25 00:49 ]

ナンさん、jamさんレスありがとうございます。
シグナルをキャッチしてロックファイルを殺すということができるとしても他ユーザが作成したロックファイルかもしれませんので
これを使用とするとロックファイル名に相手のアドレスをつけるみたいなことをするということでしょうか?
現在、対応策として、ロックファイル処理をやめて、WWWサーバのほうの設定で単一スレッドのみ実行というのをしています。
ですが、WWWサーバの仕様で同一ユーザによる連続実行時(ボタンの連打)やブラウザの戻るボタンをクリックされた時はやはり強制終了されるようです。ファイルを更新中に強制終了するとファイルというのは壊れたりするのでしょうか?ナンさんの言われたようにやはりファイルバックアップ機能を充実させるしかないのでしょうか?


4:[質問] ゆいちゃん [MAIL] [2002/05/30 11:20 ]

ナンさんjaさんすみません。
なんかうまく表示されず、返事が遅れてしまって・・・
もう一度、入れてみます。
内容は1つ上を見てください。


5:[回答] jam [2002/05/30 13:47 ]

>これを使用とするとロックファイル名に相手のアドレスをつけるみたいなことをするということでしょうか?
ロックファイルにプロセスIDをつけるとか・・・でもwinって$$まともにつかえますかね?


6:[質問] ゆいちゃん [MAIL] [2002/05/30 22:16 ]

$$ ??ってなんでしょうか?ちょっとわからない・・・です。
ごめんなさい。
それよりシグナルをキャッチするなんてこと本当にできるのですか?
WWWサーバが強制終了するシグナルをキャッチしてWWWサーバの強制終了を遅らせて、ロックファイルを削除してから強制終了なんてことが本当に?もしできるのであれば、ロックファイルにIPアドレス
をつけて作ってもいいのかもしれません。ですがそのようなことが
できるのなら強制終了を止めさせることもできるのではないでしょうか?おそらく私には難しいことなのでしょうが・・・簡単にできれば、いいのですが・・・


7:[回答] あいよ! [2002/05/31 01:29 ]

Perlメモを見てみたらどうです?
まず解決すると思いますよ。
http://www.din.or.jp/~ohzaki/perl.htm#File_Lock


8:[回答] jam [2002/05/31 10:15 ]

$$はスクリプトを実行しているPerlのプロセス番号が入っているもんです。

シグナルは%SIGハッシュにいろんな種類のシグナルがあるようです。
$SIG{INT}やら$SIG{ALRM}やらいろいろと・・・
そんでそいつらに関数のリファレンスを入れといてやると
対応するシグナルが送られてきたとき関数が実行されるという
ことです。


9:[回答] やじうま1号 [2002/05/31 16:59 ]

>>8

Winだと$$使ったところで使い物になりませんね。 Tripodでも駄目でした。(何の情報?)


10:[回答] ゆいちゃん [MAIL] [2002/05/31 23:59 ]

あいよ!さんへ
コードの方はよくわかりませんでしたが、内容はとてもよくわかりました。勉強になりました。ありがとうございます。
変な質問ですが、これって、CGI.pmを使っているのでしょうか?
明日にでも、挑戦してみようと思います。
jamさんへ
API関数みたいなものでしょうか?
ということは、Windowsにロックファイルを削除させると言うことですか?ってちがいますよね。ちょっとわからないので明日%SIGハッシュを調べてから返事します。

ちょっと自分の実力のなさに涙が出てきました。
勉強するしかありません。がんばります。
また、よろしくお願いします。


11:[質問] ゆいちゃん [MAIL] [2002/06/01 16:11 ]

あいよ!さんへ
CGI.pmは使っていないのですね。
今、挑戦しているのですが、
http://www.din.or.jp/~ohzaki/perl.htm#File_Lock
の処理をただ単に入れただけでは動かないのでしょうか?
(dir と basename だけ 値を変えて、実際にロックを入れる場所と、アンロックする場所に処理を追加しています。)
これを、すると内部サーバエラーが出てしまいます。
なにがおかしいのでしょうか?
よろしく、お願いします。


12:[回答] TOM neko [2002/06/01 18:29 ]

ロック部分は元に戻して、以下をロックファイル作成直後に入れてみたらどうなりますか。
($lockfileにはロックファイルのパスね)

$SIG{HUP} = $SIG{INT} = $SIG{PIPE} = $SIG{QUIT} = $SIG{TERM} = \&unlock;
sub unlock {
   unlink($lockfile);
   exit;
}

厳密には、ロックファイル作成と上記部分との間に来たシグナルは防げませんが、Winでプロセス番号が取得できないらしいので、とりあえずの対策。

参考:http://homepage1.nifty.com/glass/tom_neko/web/web_04.html#signal


13:[質問] ゆいちゃん [MAIL] [2002/06/01 20:40 ]

TOM neko さんへ
ロック部分を元に戻してロックファイル作成直後に
$SIG{HUP} = $SIG{INT} = $SIG{PIPE} = $SIG{QUIT} = $SIG{TERM} = \&unlock;
を入れてみたところ、うまく動作しないようです。
詳しく話すと、unlock処理をコメントにして正常動作を確認
したところ、ロックファイルは残るはずなのですが、ロックファイルは作成されずに終了していました。よくわかりませんが、ロックファイルを作成した直後に
$SIG{HUP} = $SIG{INT} = $SIG{PIPE} = $SIG{QUIT} = $SIG{TERM} = \&unlock;
によりロックファイルが削除されていると思われます。
よろしくお願いします。


14:[回答] TOM neko [2002/06/01 21:57 ]

なるほど、通常でもなにかシグナルが来てるんですね。
$SIG{INT} = $SIG{TERM} = \&unlock;
と、短くしてみたらどうでしょう。要は、強制終了するときに送られてくるシグナルだけ捕まえればいいわけです。これでだめなら5種類のシグナルを入れ替えて実験してみてください。


15:[質問] ゆいちゃん [MAIL] [2002/06/02 00:01 ]

TOM neko さんへ
$SIG{INT} = $SIG{TERM} = \&unlock;
としてみても個々に(5種類)してみても、やはりunlockは動いているようです。(この行をコメントにするとロックファイルは作成されます。)
どれが何のシグナルか、正常時のシグナルの値を把握しなければ
ならないですね。
もし、このシグナルのロック解除とあいよ!さんの教えてくれた
renameの排他制御を組み合わせれば、ほぼ排他制御に関しては
大丈夫そうですね。
明日、シグナルを調べてみます。
また、何か提案があったら教えてください。
よろしくお願いします。


16:[回答] TOM neko [2002/06/02 01:03 ]

それなんかおかしいですね。シグナルが来てるのに正常終了なんですよね?
記述が違うのかもしれません。ロック部分だけここに書いてみてください。

先のrenameの排他制御は、何らかの理由で残ったロックファイルを消すことができますから、通常それだけで十分です。シグナルの方は、今回の強制終了対策ってことで、こっちの方が簡単だと思ったのですが。


17:[質問] ゆいちゃん [MAIL] [2002/06/02 11:20 ]

TOM neko さんへ

# ロック開始〜ロック解除まで-----------------------------------
# ロック開始
&cmn_lock("$cmn_lockfile_touroku");
$SIG{HUP} = $SIG{INT} = $SIG{PIPE} = $SIG{QUIT} = $SIG{TERM} = \&unlock;
  ####更新処理####
# ロック解除
&cmn_unlock("$cmn_lockfile_touroku");
#------------------------------------------------------------

# ファイルロック処理  #
sub cmn_lock {
 if ( $cmn_lock_flg eq "1" ) {
   local($flag) = 0;
   foreach (1 .. 5) {
     # ロックファイルがあれば 1秒待つ
     if (-e $_[0]) { sleep(1); }
     # ロックファイルが無ければ生成する
     else {open(LOCK,">$_[0]");
        close(LOCK);
        # ロックに成功したら旗を立ててループを抜ける
        $flag = 1;
        last;
     }
   }
   # 旗が立たなければロック失敗と見なしてエラー
   if ($flag == 0) { &cmn_error("Lock Error:ただ今、混雑しています。少し時間をおいてもう一度お願いします。","lock"); }
 }
}

# ファイルロック解除  #
sub cmn_unlock {
 if ( $cmn_lock_flg eq "1" ) {
   # ロックファイルがあれば削除する。
   if (-e $_[0]) { unlink($_[0]); }
 }
}

とこのようになっています。ファイルロック処理、ファイルロック解除処理はrequireされた別ファイルからの呼び出しとなっています。
よろしく、お願いします。


18:[質問] ゆいちゃん [MAIL] [2002/06/02 11:57 ]

TOM neko さん
先ほど、以下の処理を入れて、もう一度試してみたら、
$SIG{HUP} = $SIG{INT} = $SIG{PIPE} = $SIG{QUIT} = $SIG{TERM} = \&unlock;
(ロックファイル削除処理はコメント)
ロックファイルは作成されました。
ブラウザに情報が残っていたようです。
お手数をおかけしました。本当に、ごめんなさい。
以後、気をつけます。
ですが、ロックファイル削除処理のコメントをはずして強制終了時にロックファイルが残らないか試してみたところ、
やはり、ロックファイルは残るようです。
よろしく、お願いします


19:[回答] TOM neko [2002/06/02 12:09 ]

$SIG{HUP} = $SIG{INT} = $SIG{PIPE} = $SIG{QUIT} = $SIG{TERM} = \&unlock;

の下に、

sub unlock {
unlink($cmn_lockfile_touroku);
exit;
}

を入れて下さい。通常のロックファイル削除とは別に用意します。(本当はロック処理のサブルーチンに入れた方がすっきりしますが)


20:[回答] ゆいちゃん [MAIL] [2002/06/02 14:01 ]

TOM neko さんへ
こんな感じですか?
#-----------------------------------------------------------------------------
#------------#
#  更新処理  #
#------------#
sub upd_prof {
    # ロック開始
    &cmn_lock("$cmn_lockfile_touroku");
    $SIG{HUP} = $SIG{INT} = $SIG{PIPE} = $SIG{QUIT} = $SIG{TERM} = \&unlock;
sub unlock {
    unlink($cmn_lockfile_touroku);
    exit;
}
    #**********#
    #<更新処理>#
    #**********#
    # ロック解除
#    &cmn_unlock("$cmn_lockfile_touroku");

    # 更新完了メッセージの表示
    &cmn_end_mes("更新完了しました。");

    exit;
}

#-----------------------------------------------------------------------------
#---------------------#
# ファイルロック処理  #
#---------------------#
sub cmn_lock {
    if ( $cmn_lock_flg eq "1" ) {
        local($flag) = 0;
        foreach (1 .. 5) {
            # ロックファイルがあれば 1秒待つ
            if (-e $_[0]) { sleep(1); }

            # ロックファイルが無ければ生成する
            else {
                open(LOCK,">$_[0]");
                close(LOCK);
                # ロックに成功したら旗を立ててループを抜ける
                $flag = 1;
                last;
            }
        }
        # 旗が立たなければロック失敗と見なしてエラー
        if ($flag == 0) { &cmn_error("Lock Error:ただ今、混雑しています。少し時間をおいてもう一度お願いします。","lock"); }
    }
}

#---------------------#
# ファイルロック解除  #
#---------------------#
sub cmn_unlock {
    if ( $cmn_lock_flg eq "1" ) {
        # ロックファイルがあれば削除する。
         if (-e $_[0]) { unlink($_[0]); }
    }
}


(正常に動いた時にロック処理のサブルーチンに入れることにします。)
このようにしてロック削除処理をコメントにすると正常終了時には正しくロックされて終了されますが、
ロック削除処理のコメントを取って強制終了時の反応を見てみるとやはり、
以前と同じくロックファイルが残ったままになります。
ちなみに、正常時のシグナルを調べてみるとすべてのシグナル共通でcode(0x1654f7c)が入っていましたが
強制終了時に個々に何が入っているかは、どのようにして取ればよいのかわからなかったため確認していません。
それとシグナルのハンドリングを調べてて以下のサイトで
http://www.himawari.sakura.ne.jp/~suzune/special/199911/07.html
>シグナルは UNIX の標準なので Windows でちゃんとハンドリングされるかは Perl インタプリタの実装によります。
と書いてありました。やはりwindows環境では取れないのでしょうか?
ご迷惑おかけしますが、よろしくお願いします。


21:[回答] ゆいちゃん [MAIL] [2002/06/02 14:32 ]

TOM neko さんへ
ActivePerlのページにシグナルハンドラがサポートされていない
という記述がありました。詳細は以下です。
http://plaza27.mbn.or.jp/~satomii/jdoc/ActivePerl/Perl-Win32/perlwin32faq5.html#Signal_Handling


22:[回答] TOM neko [2002/06/02 16:37 ]

>>21
そこはすでに読みましたが、ActivePerlのサイトでは使えるような事が書いてあるのです。
まあ、とにかくダメだったので、すぐに次の手。rename式ロックですね。こちらは強制終了時にロックを消すのではなく、後から来たプロセスが、ロックが残っていれば消す方式です。

http://www.din.or.jp/~ohzaki/perl.htm#File_Lock
のsub my_flockとsub my_funlockをそっくりコピーして、sub cmn_lockとsub cmn_unlockを削除して(名前が違うので残してもかまいませんが)サブルーチンを入れ替えます。

呼び出しは、以下のように。タイムアウト時間(ロックを残す時間)を短かめの20秒にしてみました。
sub upd_prof {
# ロック開始
   $cmn_lockfile_touroku =~ m|^(.*)([^/]+)$|; # ロックファイルのパス分解
&$lfh = my_flock(dir => $1, basename => $2, timeout => 20) or &cmn_error("Lock Error:ただ今、混雑しています。少し時間をおいてもう一度お願いします。");

#**********#
#<更新処理>#
#**********#

# ロック解除
&my_funlock($lfh);

# 更新完了メッセージの表示
&cmn_end_mes("更新完了しました。");

exit;
}


23:[質問] ゆいちゃん [MAIL] [2002/06/02 18:27 ]

TOM neko さんへ
上記を入れて正常時の動きを試してみました。
そうすると、内部サーバエラーで落ちてしまいました。
そこで、調べてみると、
>$cmn_lockfile_touroku =~ m|^(.*)([^/]+)$|; # ロックファイルのパス分解
のところで、実際には
$cmn_lockfile_touroku = '../../lock/touroku.lock'
が入っています。
分解された結果には $1 eq "../../lock/touroku.loc"、$2 eq "k"
が入っていました。これが原因なのでしょうか?(正規表現は苦手です。)
このRENAME処理を追加するにあたっての注意点みたいものはありますか?
lockフォルダの中には常にtourokuファイルがないとダメなのでしょうか?
よろしくお願いします。


24:[回答] TOM neko [2002/06/02 18:57 ]

あ、失敗です。
$cmn_lockfile_touroku =~ m|^(.*/)([^/]+)$|;
でした。
この方法では、lockフォルダの中にtouroku.lockファイルが常にあって、touroku.lock987654321のように後ろに作成時刻がついた状態がロック状態ですね。
あと、タイムアウトが20秒にしたのは短いかもしれません。


25:[質問] ゆいちゃん [MAIL] [2002/06/02 20:24 ]

TOM neko さんへ
上記で$1、$2は正しく取れたようです。
touroku.lock987654321というようなファイルは作成されましたがその後に
内部サーバエラーで落ちます。

 for (my $i = 0; $i < $lfh{trytime}; $i++, sleep 1) {
   return \%lfh if (rename($lfh{path}, $lfh{current} = $lfh{path} . time));
 }


調べた結果、rename関数を呼び出してファイルを作ると同時に落ちているようです。
もうちょっとでできそうなのですががんばってみます。
よろしくお願いします。


26:[回答] TOM neko [2002/06/02 21:49 ]

ここの頭、

&$lfh = my_flock(dir => $1, basename => $2, timeout => 20) or &cmn_error("Lock Error:ただ今、混雑しています。少し時間をおいてもう一度お願いします。");

&は、いりませんね。取ってください。

$lfh = my_flock(...


27:[完了] ゆいちゃん [MAIL] [2002/06/02 22:51 ]

TOM neko さんへ
できました。
正常時の正しくロックファイルが作成、削除
異常終了時の後プロセスのロックファイル削除処理が確認できました。
ものすごく勉強になりました。(排他制御)
とりあえずの対策としてはこれでいけると思います。
シグナルハンドラの件は、できるかもということなので、
また、別に調べてみます。(これができれば、WWWサーバからの強制終了後すぐに
ロックの削除ができるのでこれとrenameで完璧になりますね。)
調べても、わからない時はまたここに書き込みます。
そのときはまたよろしくお願いします。
あっでもわかった時はメールでもください。よろしくお願いします。
迷惑ばっかりでもっと勉強しなくてはいけませんね。
長々と本当にありがとうございました。


28:[完了] TOM neko [2002/06/03 18:44 ]

ロック呼び出しの
timeout => 20
の部分は、ロックが作られてから強制削除(rename)対象にされるまでの時間(秒)です。不正なロックかどうかの判断は、ひとえに生存時間の長さにかかっています。<更新処理>にかかる時間より長くしないと、正常なロックも消してしまいます。サーバの込み具合によって調節してください。

強制終了後に待たせないようにと20秒にしましたが、短いような気がします。もし強制終了することが無くなったら、ロックが残るのも稀なことになりますから、10分(600)以上にしても問題ないでしょう。

シグナルハンドラは、今回の強制終了ではダメだったのです。シグナルハンドラ自体が使えないのか、シグナルの種類によるのか、この強制終了ではシグナルが出ないのか、どれかわかりませんが使えないことはハッキリしてしまいました。

なので、残念ですが「完璧」にはなりそうもありません。


29:[完了] ゆいちゃん [MAIL] [2002/06/03 23:30 ]

TOM neko さんへ
>timeout => 20
今は30で設定しています。実際に6万件ぐらいのデータの入っているファイルの更新しても、
20もかからなかったからなのですが、webサーバ上で動きを見るのと、アクセスユーザから動きを
見るのとでは、処理時間は変わるのでしょうか?ネットがつながれば試してみるつもりですけど。
(現在はairH"を使ってネットを見てます。)
私も、timeoutをどれくらいにするか悩んでました。
ボタンを2回連続クリックされただけで強制終了ですから、度々起こるだろうし、
timeoutを長くするのも待たせることになるし。
シグナルハンドラができればこれも悩まずにすんだのでしょうけど、一日中調べてみましたが、
やっぱりダメでした。sigintは絶対ダメということはわかりましたけど。
まぁ勉強になったのでよっかったですが
あとはAN HTTPの対応しだいですね。そうすれば、
>timeout => 600
にできますね。
完璧ではありませんが、完璧に近づきました。
これも、TOM neko さんのおかげです。
本当にありがとうございました。


30:[完了] TOM neko [2002/06/04 01:42 ]

>webサーバ上で動きを見るのと、アクセスユーザから動きを
見るのとでは、処理時間は変わるのでしょうか?

それは、同時に何人アクセスするかによりますね。ですから、ネットに繋がってなくて一人だけのアクセスだと速いです。独立したサーバだったら、他のサイトの影響を受ける事はないですけど。ロックを作ってから消すまでの時間ですから、途中経路の混雑は関係ないです。


そこまで必要ないとは思いますが、時間帯によってロック時間を変えるなんて事もできますね。


31:[完了] ゆいちゃん [MAIL] [2002/06/04 11:51 ]

TOM neko さんへ
とうとう30まできてしまいました。
ここまでやさしく指導していただいてありがとうございます。
>時間帯によってロック時間を変えるなんて事もできますね。
これで、やってみようと思います。
これからもよろしくお願いします。

回答(必須): 状態:

お名前(必須):

e-mail:

URL:




[戻る]

ChaichanPAPA's World