- PR -

xinetdの設定でwait=yesとした場合のサービスの動き方

1
投稿者投稿内容
d@
会議室デビュー日: 2007/01/20
投稿数: 10
投稿日時: 2008-02-13 02:34
xinetdのシングルスレッドモードの扱い方について教えてください。

RHEL 4.5 Update 5にてJavaとC++が連携動作するアプリケーションを開発しています。
そのアプリでJavaとC++で共通のログファイルを使用したいというオーダーがあり、
JavaとC++の結合密度を弱めるべく、ログ出力はxinetdを介してPerlのスクリプトでファイル出力することになりました。

具体的には

(1)(Java or C++の)アプリからソケット通信でlocalhostの指定ポート番号に接続
(2)電文でメッセージを送信
(3)xinetdがPerlのログ出力スクリプトに電文メッセージを流す
(4)Perlスクリプトがログ書込処理を実施する
(5)ソケット通信のコネクションをクローズする

という流れを考えています。

・・・そこで問題になっているのが、同時に複数プロセスからログ書き込み要求があった場合
(JavaとC++から同時にソケット通信の接続が発生した場合)の排他制御です。

xinetdの設定項目でwait=yesとするとシングルスレッドで動作するということなので、
下記の設定ファイルをxinetd.d配下に追加してデーモンを起動すると、LISTENはするのですが
ログ出力は行われませんでした。(Perlスクリプトが呼び出されていないようです)

コード:

service commonlog
{
 flags = REUSE
 socket_type = stream
 wait = yes
 user = root
 server = /script/commonlog.pl
 log_on_failure += USERID
 disable = no
}



そこで詳しく調査したところ、
http://www.linux.or.jp/JM/html/xinetd/man5/xinetd.conf.5.html にて
「wait この属性はサービスがシングルスレッドか、マルチスレッドかを決定する。値が yes ならシングルスレッドである; すなわち xinetd は、サーバーを起動したらそのサーバが死ぬまでは、そのサービスへの要求に対する処理を停止する。値が no ならサービスはマルチスレッドであり、 xinetd はサービスへの新たな要求を処理し続ける。」
という記述に行き当たりました。

この「サーバーを起動したらそのサーバが死ぬまでは」という文言の意味がわからず悩んでいます。
ログ出力完了後、ログ出力プロセスをkillしてデーモンを再起動しなければ
新しいコネクションの接続要求を受け付けることができないということでしょうか?
ソケット通信クライアントがコネクションをクローズすると、サーバ側は新規コネクション受付可能状態になる・・・という感じではないのでしょうか?
このあたりの挙動がどうにもよくわかりません。
wait=yesの設定は本来どのように使用すべきものなのか等も含めて教えていただけると助かります。

なお、試しに、wait=noとすると、ログ出力することができたので
アプリケーション側(ソケット通信クライアント側)の問題というわけではないと思います。
angel
ぬし
会議室デビュー日: 2005/03/17
投稿数: 711
投稿日時: 2008-02-13 13:01
こんにちは。

xinetdから起動されるプロセスの動作仕様のお話として、基本的に、シングルスレッドモードとマルチスレッドモードに差は無い、というのが私の認識です。
※もちろん、サービス的には並行処理されない・されるの違いがありますし、プロセス多重起動による排他を考える必要がない・あるの違いはありますが。

なので、現時点では、
引用:
なお、試しに、wait=noとすると、ログ出力することができたので
アプリケーション側(ソケット通信クライアント側)の問題というわけではないと思います。

という判断は時期尚早でしょう。

Perlスクリプトはどのような造りでしょうか?
xinetdから起動される以上、wait=yes/noに関わらず、

 ・ログファイルを追記モードで開く
 ・標準入力(=xinetdが用意したソケット)から電文を読む
 ・ログファイルに書き込む
 ・電文の終わり(もしくはクライアントからのshutdown)を検知したらプロセスを終了する。
  ※必要に応じてログローテートを実施する

という形になるはずですが…。

※蛇足ですが、ログの処理で xinetd を使うのは大丈夫でしょうか?
 ログの1エントリ毎にログ書き込みプロセスの起動・終了が絡むので、ログが頻繁に書かれる状況だと重くなりますが…。
 単純にソケットを処理するスクリプトを書いた方が早いような気がします。

[ メッセージ編集済み 編集者: angel 編集日時 2008-02-13 13:12 ]
d@
会議室デビュー日: 2007/01/20
投稿数: 10
投稿日時: 2008-02-14 02:11
早速のアドバイスありがとうございます。

Perlのスクリプトについてですが、概ねangel様の仰るような処理フローになっています。
(サンプルと言えど、ソースコードやドキュメントの持ち出し不可能な環境で作業しているため、掲示板へのコピペはできそうにないです・・・申し訳ないです。)

3つ目の「電文の終わり(もしくはクライアントからのshutdown)を検知したらプロセスを終了する。」という点に関してですが、現行のPerlスクリプトでは

コード:

while (<STDIN>) {
    print FILE "$_";
}


とすることで標準入力を読んでいます。
ソケット通信のクライアントがコネクションをクローズ→標準入力への入力がストップ→Perlスクリプトの実行プロセスは上記whileブロックを抜ける・・・という認識でいたのですが、電文の終わりを検知する処理が別途必要なのでしょうか?

(実は私、入社以来ずっとJ2EEのWebアプリプログラマだったもので、こういうOSネイティヴの機能やネットワークを使用したプログラムを書くのは初めてなのです・・・きっとそのあたりで常識知らずの勘違いをしているような気がしているのですが・・・)

あと、ご指摘のあったパフォーマンスの問題に関してですが、
書き込み要求が頻繁に発生するような類のアプリではないので特に心配していないです。何十分に一度出力される程度のログなので「排他制御を実装するのは面倒だからxinetdにやらせてしまおう!」とかそんな程度です。
どうにもならないようであれば、wait=noでマルチスレッドモードで動かして、perl側でflockか何かで排他制御をかけてしまおうかなと思っています。
angel
ぬし
会議室デビュー日: 2005/03/17
投稿数: 711
投稿日時: 2008-02-14 02:22
こんばんは。

そうすると、クライアント側の書き込み処理についても確認した方が良いかも知れません。
コードを見る限りでは、クライアントがshutdownすれば、サーバ側も読み込みのループを抜けてプロセスを終了することになります。

であれば、クライアントがログ書き込み終了時にshutdown ( もしくは close ) をしていない可能性も疑った方が良いでしょう。

各プロセスに対し、strace でシステムコール実行状況を見るか、lsof で使用しているファイルディスクリプタ ( 含む socket ) を調査してみるのが良いように思います。

wait=no でログ書き込みが行われない、となると、xinetd から起動されたスクリプトが、何らかの原因でブロックしたまま終了しない、という状況が考えられますので。
d@
会議室デビュー日: 2007/01/20
投稿数: 10
投稿日時: 2008-02-20 21:02
返事が遅くなって申し訳ありませんでした。

Java/C++のコードを再確認しましたが、
やはりきちんとshutdown(close)し、できなければ例外を送出する動きになっていました。

というわけで、本日再び動作確認が取れる環境になったので、再度試験を実施したのですが・・・

なんと、wait=yesで正常にログが出力されてしまいました。
同時に複数スレッドから大量にログ出力処理を実施しても、
問題なく意図通りの結果を得ることができました。

目下、最初に試験したときに動かなかった原因を調査中ですが、
最初の試験実施後にサーバーの再起動を行ったのと、
LISTENしていることの確認のため、ログ出力受付用ポートにtelnetをかけたので
ひょっとすると、そのtelnetがスレッドを開放せず、ずっと標準入力受付状態になっていたのかもしれない・・・と考えています。
(それで、wait=noとしてxinetdを再起動したらログが出力された???
何にせよ、設定実施時のヒューマンミス???・・・お恥ずかしい限りです)

angel様、深夜にもかかわらずアドバイスありがとうございました。
本当に助かりました!心から感謝します!!

[ メッセージ編集済み 編集者: 未記入 編集日時 2008-02-20 21:08 ]
1

スキルアップ/キャリアアップ(JOB@IT)