すべての良い一日。
最近、サービスの問題として、シェル制御スクリプトを使用してJavaでIBM AIX 5.2のバックグラウンドプロセスを記述することが必要になりました。
作業が完了すると、ドキュメントが完成し、コードが作成されます。それを公開してみませんか? これによると、私たちはビジネスに取りかかります。
1悪魔
アプリケーションを悪魔化するには、アプリケーションを直接起動する端末から切断する必要があります。 これを行うには、次の手順を実行します。
- ターミナルからstdinを切断する
- 起動行の最後にアンパサンド「&」を指定して、バックグラウンドでプロセスを開始します
- stdin、stdoutをアプリケーションで直接閉じます(この場合、Javaの場合、System.in.close(); System.out.close();)
したがって、最小の起動行は次のようになります。
java daemon_app <&-&&
ここで、「<&-」は標準入力を無効にします。
前述の「&」を使用すると、アプリケーションをフォアグラウンドモードからバックグラウンドモードに切り替えることができます。
daemon_appコードは次のようになります。
パブリッククラスdaemon_app
{
public static int main(String [] args)
{
試してみる
{
daemonize();
}
catch(Throwable e)
{
System.err.println( "スタートアップに失敗しました。" + E.getMessage());
1を返します。
}
doProcessing();
0を返します。
}
static private void daemonize()throws Exception
{
System.in.close();
System.out.close();
}
静的なプライベートvoid doProcessing()
{
//処理を行います
}
}
daemonizeメソッドは、アプリケーションをstdinおよびstdoutから切断します。 stderrストリームは唯一のアクティブなストリームであり、初期化段階でエラーを記録するために使用できます。
doProcessingメソッドは、基本的なロジックを実装するように設計されています。
次に、log4jなどの何らかのロギングフレームワークを使用できます。
次の改善点は、プロセスからstderrに送信されるデータをインターセプトするように起動ラインを変更することです。 これを行うには、次のように起動行を変更します。
java daemon_app <&-2> /var/log/daemon_app_error.log&
「2> /var/log/daemon_app_error.log」は、stderrからの出力をファイル/var/log/daemon_app_error.logにリダイレクトします。
リーダーが最新でない場合、UNIXシェルの入力/出力ストリームには次の識別子があります。
標準入力-0
標準出力-1
stderr-2
2割り込み信号処理の構成
doProcessingメソッドでは、プロセスが終了する前提条件で無限ループを編成できます。 この条件は、たとえばkill -15 <pid>を介してオペレーティングシステムから送信されるSIGTERMである可能性があります。
コード15(SIGTERM)は、AIX、HP-UX、および通常のLinuxベースのシステムの両方で同じです。
シグナルとそのコードのリストは、kill -lコマンドを使用して取得できます。
Javaでシグナルを処理するには、いくつかの方法があります。
- sun.misc.Signalおよびsun.misc.SignalHandlerを使用してから、sun.misc.SignalHandlerを実装する独自のハンドラークラスを作成します。 この方法の詳細については、 こちら (http://www.ibm.com/developerworks/ibm/library/i-signalhandling/)を参照してください。
- Runtime.getRuntime()。AddShutdownHookメソッドを使用します。 (メソッドシグネチャ:public void addShutdownHook(スレッドフック))
最初の方法は、パッケージsun.miscのクラスを使用しているため、あまり良くありません。 なんで?
Sun
がパッケージsunについて
書いているもの (http://java.sun.com/products/jdk/faq/faq-sun-packages.html)
は次のとおりです。
太陽*パッケージは、サポートされているパブリックインターフェイスの一部ではありません。
sunを直接呼び出すJavaプログラム*パッケージは、すべてのJava互換プラットフォームで動作することが保証されているわけではありません。 実際、そのようなプログラムは、同じプラットフォーム上の将来のバージョンでも動作することが保証されていません...
2番目のメソッドには、Threadから継承したクラスを引数として渡すことが含まれます。 これは、SIGTERMを受信すると、プログラマーによって定義されたアクションを実行してプロセス全体を完了する新しいスレッドが作成されることを意味します。
ドキュメント (http://java.sun.com/docs/books/jvms/second_edition/html/Concepts.doc.html#19152)に目を向けると、Javaプログラムを完了するために必要な前提条件は何ですか?
Java仮想マシンは、すべてのアクティビティを終了し、次の2つのいずれかが発生すると終了します。
- デーモンスレッド(§2.19)以外のすべてのスレッドは終了します。
- 一部のスレッドは、クラスRuntimeまたはクラスSystemのexitメソッドを呼び出し、セキュリティマネージャーによってexit操作が許可されます。
したがって:
- デーモン以外のスレッドを実行している場合、正しい完了を確認し、結合を使用してメインにアタッチする必要があります。 デーモンスレッド、つまり setDaemon(true)が実行されたものを停止する必要はありません。
- スレッドメインを停止します。つまり、起動されたクラスのメインメソッドを終了します。
プログラムを完了するために特定の操作を実行する必要がない場合は、System.exit()を使用して、使用中のリソースとアクティブな接続を閉じます。
Runtime.getRuntime()を使用して変更されたコード。AddShutdownHookを使用して割り込みハンドラーを作成する方法を以下に示します。
パブリッククラスdaemon_app
{
static private boolean shutdownFlag = false;
public static int main(String [] args)
{
試してみる
{
daemonize();
}
catch(Throwable e)
{
System.err.println( "スタートアップに失敗しました。" + E.getMessage());
1を返します。
}
registerShutdownHook();
doProcessing();
0を返します。
}
静的なプライベートvoid doProcessing()
{
while(false == shutdownFlag)
{
//処理を行います
}
}
static public void setShutdownFlag(){shutdownFlag = true;}
private static void registerShutdownHook()
{
Runtime.getRuntime()。AddShutdownHook(
新しいスレッド(){
public void run(){
daemon_app.setShutdownFlag();
}
}
);
}
static private void daemonize()throws Exception
{
System.in.close();
System.out.close();
}
}
したがって、registerShutdownHookメソッドがあります。これは、mainから呼び出され、割り込み信号のハンドラーを登録します。
割り込み信号を受信すると、静的メソッドsetShutdownFlagが呼び出され、静的ブールプロパティshutdownFlagの値がtrueに変更されます。この値は、前提条件でdoProcessingメソッドのループを構成します。
3制御スクリプト
したがって、デーモンプロセスが記述されます。 次に、開始、停止、ステータスの監視を制御するスクリプトを作成する必要があります。
シェル制御スクリプトを作成するプロセス全体を説明するのではなく、いくつかの有用な手順のみを説明します。
プロセスの開始/実行に必要な環境変数を確認する例
引数なしのプロシージャ。 forループを使用して、必要な環境変数を繰り返し処理します。 変数がない場合、警告が表示されます。 少なくとも1つの変数が設定されていない場合、エラーコード1で実行を停止します。
check_env()
{
exit_flag = 0
JAVA_HOME ORACLE_HOME TUXEDO_HOMEのenv_varの場合。
する
eval "env_value = \ $$ env_var"
if [-z "$ env_value"]
それから
echo "エラー:環境変数 '$ env_var'が設定されていません"
exit_flag = 1
fi
やった
if [$ exit_flag -eq 1]
それから
echo「終了しました。プロセスは開始されませんでした」
1番出口
fi
}
クラスパス要素の確認
このプロシージャへの引数は、要素がコロンで区切られたクラスパスを持つ行です。
コロンを空白文字に置き換えます。その結果、各要素をチェックする機会が得られます。
check_classpath()
{
#クラスパス内のチェックファイルが存在し、読み取り可能
`echo $ 1のリソース用| sed -e "s /:/ / g" `;
する
if [$ {#resource} -gt 0] && [! -r $ resource]#ファイルが存在しないか読めない場合
それから
echo "警告:CLASSPATHに含まれるリソース '$ resource'が存在しないか、読み取り不能です"
fi
やった
}
プロセス開始手順の例
この手順は、設定変数-PID_DIR、PROCESS_NAME、CPATHの存在を提供します
どこで
PID_DIR-pidファイルを保存するためのディレクトリ
PROCESS_NAME-プロセス名
CPATH-クラスパスのある行
バックグラウンドで実行されているプロセスの識別子は、シェル変数「$!」を使用して決定できます。シェル変数は、後でpidファイルに書き込まれます。
その後、5秒間チェックが行われるか、プロセスが起動プロセス中に「落ちなかった」。 1秒ごとに、ps -pを使用してプロセスのステータスがチェックされます。
launch_daemon()
{
JVM_OPTS = ""
#最大メモリを1Gに設定
JVM_OPTS = "$ JVM_OPTS -Xmx1G"
echo "開始プロセス\ c"
#入力ストリームを閉じた状態でプロセスをバックグラウンドで実行し、端末から切り離します
$ JAVA_HOME / bin / java $ JVM_OPTS -cp $ CPATH daemon_app <&-2> /var/log/$PROCESS_NAME.pid&
#pidをpidファイルに書き込む
エコー$! > $ PID_DIR / $ PROCESS_NAME.pid
if [$ {#!} -eq 0]
それから
echo "... [失敗]"
else#プロセスが動作している場合は5秒間チェックする
タイマー= 0
while [$ timer -lt 6]
する
エコー "。\ c"
寝る1
タイマー= $タイマー+ 1
やった
if [`ps -p $! | wc -l` -gt 1]
それから
エコー「[開始]」
出口0
他に
エコー「[失敗]」
1番出口
fi
fi
}
プロセス停止手順の例
指定された実装は、2つの入力引数を取ります。
プロシージャはSIGTERMプロセスを送信し、5秒待機した後、プロセスが停止していない場合はSIGKILLを送信し、その後pidファイルを削除します。
stop_daemon()
{
echo "プロセスの停止\ c"
タイマー= 0
if [`ps -p $ 1 | wc -l` -eq 1]
それから
エコー「実行されていません」
他に
殺す-TERM $ 1
while [`ps -p $ 1 | wc -l` -gt 1]
する
if [$ timer -gt 5]
それから
殺す-KILL $ 1
タイマー= 0
他に
エコー "。\ c"
寝る1
fi
タイマー= $タイマー+ 1
やった
エコー「停止」
fi
rm $ 2
}
プロセス状態検証手順の例
1つの引数を取ります-pidファイルへの絶対パス
プロセスが実行中および実行中の場合は0以外の値を返し、プロセスが非アクティブの場合は0を返します。
check_running()
{
if [-e $ 1]
それから
fpid = `cat $ 1`
if [$ {#fpid} -gt 0]
それから
lines = `ps -p $ fpid | wc -l`
echo $(($ lines-1))
他に
エコー0
fi
他に
エコー0
fi
}
基本的に私が伝えたかったことはそれだけです。
このトピックで誰が何を考えているのかを聞くのは興味深いです。