言語としてのJavaは、ユーザーがコードを開発できるように設計されており、ゆえにアプリケーションクラッシュという結果も導くことにもなります。 いかなるエラーも、投げられた例外をキャッチして適切に処理するというのは明白であるということです。

Java開発者やシステム管理者はご存知のとおり、Java仮想マシン(JVM)自身がクラッシュしうるというのが現実です。 その理由は、JVM自身もネイティブコードで書かれたプログラムであり、巨大で複雑なプログラムと同様に、JVM自身にもバグがあるからです。

いかなるプログラムと同様に、Javaがクラッシュすると、そのJavaアプリケーションも一緒に簡単に落ちます。 もしアプリケーションが基幹システムだったりすると、これは悲惨なことです。 アプリケーションがダウンした場合、ユーザーが利用不具合に気づいて連絡するまで、そのシステム停滞が通知されない可能性もあります。 さらにアプリケーションがダウンしているという現実を検知してから、システム管理者が修復するのに1、2時間の時間を要することもあります。

ソリューション

この問題のソリューションは、Java動作について知り尽くしているモニタリング(監視)アプリケーションを導入し、 24時間365日、アプリケーションのモニタリング(監視)を安心して任せ、問題が発生したときに自動的に処置をしてくれることです。

Java Service Wrapperには、いくつかのモニタリング(監視)機能が含まれています。 その機能の1つが、JVMクラッシュを検知する機能です。 Wrapperは、JVMを定期的にモニタリング(監視)して、JVMクラッシュを即座に認識することができます。 クラッシュを検知すると、Wrapperは自動的にJVMを再起動して、 最短の停滞時間でシステムをリカバリー(回復)させることができます。 さらに、メール通知機能を設定すれば、 Wrapperからシステム管理者へ状況レポートをメール送信することができるため、 全てが元通りにリカバリー(回復)し、通常どおり動作していることを再確認することができます。

クラッシュ検知、リカバリー(回復)、メール通知の処理など、人員的な作業は不要で、全てはWrapperが自動処理してくれます。

クラッシュを検知すると、Wrapperはログファイルに次のような記録をとります:

ログ例:クラッシュとレポート機能
jvm 1    | ...
jvm 1    | #
jvm 1    | # A fatal error has been detected by the Java Runtime Environment:
jvm 1    | #
jvm 1    | #  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00000001800074ff, pid=856, tid=704
jvm 1    | #
jvm 1    | # JRE version: 6.0_24-b07
jvm 1    | # Java VM: Java HotSpot(TM) 64-Bit Server VM (19.1-b02 mixed mode windows-amd64 compressed oops)
jvm 1    | # Problematic frame:
jvm 1    | # C  [wrapper.dll+0x74ff]
jvm 1    | #
jvm 1    | # An error report file with more information is saved as:
jvm 1    | # C:\myapp\bin\hs_err_pid856.log
jvm 1    | #
jvm 1    | # If you would like to submit a bug report, please visit:
jvm 1    | #   http://java.sun.com/webapps/bugreport/crash.jsp
jvm 1    | # The crash happened outside the Java Virtual Machine in native code.
jvm 1    | # See problematic frame for where to report the bug.
jvm 1    | #
wrapper  | JVM が予想外に終了しました。
wrapper  | JVM起動中...
jvm 2    | WrapperManager: 初期化中...
jvm 2    | ...

上記のメッセージが少し複雑に見えますが、クラッシュについての情報を提供することがJVMの意図とする試みです。 JVMから一番低いレベルで、この出力がコンソールへダンプされるため、 いかなるJavaベースのログ化ツールでも、この情報を記録するのは不可能です。 Wrapperは、JVMのコンソール出力の100%をキャッチすることができ、 全てをログファイルに保存することができます。 Wrapperがなければ、アプリケーションがダウンしたままだけでなく、何が起きたのか、現状の把握もできないでしょう。

理想的には、クラッシュの原因を修正することですが、 多くのクラッシュ原因は、完全に有効なJavaコード内の曖昧な相互作用によるものであったり、 JVM自身が持つバグであったりするため、その問題を特定して回避策を検証するのに数週間や数ヶ月を要することもあります。

Wrapperは次のような様々な危機的な問題の検知に役立ちます: フリーズデッドロッククラッシュアプリケーションエラーメモリリークJVM終了コードに応答するなど。

テクニカル概要

Wrapperのクラッシュ検知は、クラッシュがJVMやシステムに余計な負荷をかけないていないか確認することが可能になっています。 クラッシュが起きたり起きなかったりするので、その検知機能を無効にすることはできません。 しかしながら、クラッシュが起きた場合に、どのように処理をするのか、コントロールすることは可能です。

クラッシュを検知すると、Wrapperはまずシステムメモリやリソースが通常状態に落ち着くまで、 一瞬(デフォルト値で5秒間)待機します。 その後、何か設定済みの通知を送信した後、JVMインスタンスを新たに起動して、 最短の停滞時間でシステムをリカバリー(回復)させることができます。 下記で述べるように、もし必要であれば、この自動再起動の機能を無効にすることも可能です。

体感してみる

実際のJavaアプリケーションでJVMクラッシュを再現するのは困難でしょう。 このテストを簡単にするには、Wrapperに同梱されているTestWrapperサンプルアプリケーションをご利用ください。 その他にも様々なテスト機能が含まれており、体感でお試しいただくことができます。

Wrapperをダウンロードして解凍したフォルダに行き、 コンソールからTestWrapperサンプルアプリケーションを起動してください。 Windows上では「bin\TestWrapper.bat」、 UNIXプラットフォーム上では「bin\testwrapper console」を実行してください。

TestWrapperサンプルアプリケーションが開始すると、シンプルなGUI画面が開き、左側にボタンが並んでいるのが見えるでしょう。 「ネイティブアクセス違反」ボタンクリックして、JVMをクラッシュさせてみましょう。

ログ例:クラッシュと再起動
jvm 1    | TestWrapper: ダイアログを表示中...
jvm 1    | WrapperManager: 警告: Attempting to cause an access violation...
jvm 1    | #
jvm 1    | # A fatal error has been detected by the Java Runtime Environment:
jvm 1    | #
jvm 1    | #  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x000000018000746f, pid=1632, tid=2284
jvm 1    | #
jvm 1    | # JRE version: 6.0_24-b07
jvm 1    | # Java VM: Java HotSpot(TM) 64-Bit Server VM (19.1-b02 mixed mode windows-amd64 compressed oops)
jvm 1    | # Problematic frame:
jvm 1    | # C  [wrapper.dll+0x746f]
jvm 1    | #
jvm 1    | # An error report file with more information is saved as:
jvm 1    | # C:\myapp\bin\hs_err_pid856.log
jvm 1    | #
jvm 1    | # If you would like to submit a bug report, please visit:
jvm 1    | #   http://java.sun.com/webapps/bugreport/crash.jsp
jvm 1    | # The crash happened outside the Java Virtual Machine in native code.
jvm 1    | # See problematic frame for where to report the bug.
jvm 1    | #
wrapper  | JVM が予想外に終了しました。
wrapper  | JVM起動中...
jvm 2    | WrapperManager: 初期化中...

もし、これを自分のJavaアプリケーションで試してみたい場合、 WrapperManager.accessViolationNative() をコールすることで意図的なクラッシュを引き起こすことが可能です。

トラブルシューティング

一見、Javaのクラッシュレポート報告の便利さがよく分からないと思いますが、 問題の解決に必要な役立つ情報を提供するため、何を見るべきか知る際に、便利に利用できます。

上記の例で、JVMはエラー詳細を「C:\myapp\bin\hs_err_pid856.log」に保存したことを知らせてくれます。 このクラッシュ報告の分析に関する説明は、この文書の範囲外ですが、オラクルの 『トラブルシューティングガイド』をご覧ください。 もし、もっと深入りをして本当に試してみたいなら、 『JVMクラッシュ分析におけるクラッシュコース』がかなり有益です。

再起動の遅延

対応バージョン :2.2.9
対応エディション :プロフェッショナル版スタンダード版コミュニティー版
対応プラットフォーム :WindowsMac OSXLinuxIBM AIXFreeBSDHP-UXSolarisIBM z/Linux

クラッシュを検知すると、JVMを再起動する前に、Wrapperはデフォルトで5秒間、待機して、 クラッシュしたJVMに利用されていたメモリやシステムリソースがリカバリー(回復)されるのを待ちます。 もし、検知直後にJVMを再起動すると、クラッシュの理由にもよりますが、 新しいJVMに問題が発生したり、JVMの開始が遅くなることがあります。 このシステムが落ち着くまでの時間の長さは、 システム全体の負荷やクラッシュしたJVMで動作していたアプリケーションのサイズなどを含み、多くの要因に依存します。 この遅延を短くすることは可能ですが、デフォルト値が一番安全な待機時間であることが確認されています。

JVMを再起動する前の遅延時間は、 [wrapper.restart.delay]プロパティで設定します。

設定例: JVM再起動の待機時間(5秒)
wrapper.restart.delay=5

再起動を無効にする

対応バージョン :3.3.4
対応エディション :プロフェッショナル版スタンダード版コミュニティー版
対応プラットフォーム :WindowsMac OSXLinuxIBM AIXFreeBSDHP-UXSolarisIBM z/Linux

JVMクラッシュが起きるほとんどの場面で、再起動をしたいと思うでしょう。 ある事例ケースがありますが、最初に、手作業によるクリーンアップや他のアクションが必要になる可能性もあります。 それらのケースにおいては、 [wrapper.disable_restarts.automatic]プロパティ を使って、Wrapperの再起動の機能を無効にすることができます。

設定例: (再起動を無効)
wrapper.disable_restarts.automatic=TRUE

イベントに応答する

対応バージョン :3.3.0
対応エディション :プロフェッショナル版スタンダード版 (未対応)コミュニティー版 (未対応)
対応プラットフォーム :WindowsMac OSXLinuxIBM AIXFreeBSDHP-UXSolarisIBM z/Linux

アプリケーションがフリーズしたりクラッシュしたとき、まれに単純に再起動だけで解決しないケースもあります。 もしテンポラリーファイルが正しく消されないと、あるいはデータベースが不安定のまま残ったりすると、 新しいアプリケーションのインスタンスが、すぐにエラーで落ちてしまうことがあります。 理想ならば、これらの障害に十分対応できるよう、アプリケーションがしっかりと設計されていればいいのですが、 常にそうできるものでもありません。 このようなケースでは、新しくJVMを起動する前に、クリーンアップ作業をする手段が必要です。

Wrapperは、イベントに応じたコマンドを実行することで、これに対応することができます。 様々な場面で、外部コマンドやスクリプトを実行するようにWrapperを設定することが可能です。

もしデバッグのログ出力を閲覧すると、「Enqueue Event 'NNN'」のエントリー記録があります。 これらは、Wrapperのイベントが発生したタイミングを示しています。 JVMフリーズを検知した後に再起動するケースで、新しくJVMインスタンスを起動する前にクリーンアップスクリプトを起動したいならば、 「jvm_restart」イベントを利用します。

次のようにシンプルなバッチファイルを作成して、Wrapperバイナリと同じディレクトリー内に置いてください。 この例では、単純に10秒のクリーンアップタスクを実行しています。本当の実物スクリプトの方がずっと面白いですが。

「cleanupTest.bat」のサンプルスクリプト
@echo off
echo 10秒間のクリーニングアップ中...
rem 10秒間の遅延を引き起こすpingを利用
PING 1.1.1.1 -n 1 -w 10000 >NUL
echo 全てクリーニングアップ完了。

次に、ご利用のコンフィギュレーションファイル「wrapper.conf」に次のプロパティを追加します。

「wrapper.conf」の設定
wrapper.event.jvm_restart.command.argv.1=cleanupTest.bat
wrapper.event.jvm_restart.command.block=TRUE
wrapper.event.jvm_restart.command.loglevel=INFO

では、試してみましょう。フリーズを検知すると、Wrapperは設定されたバッチファイルを実行して、 それが完了するまで、Wrapperが待機しています:

ログ出力例:
jvm 1    | # The crash happened outside the Java Virtual Machine in native code.
jvm 1    | # See problematic frame for where to report the bug.
jvm 1    | #
wrapper  | JVM が予想外に終了しました。
wrapper  | イベントコマンド「jvm_restart」: コマンドライン: cleanupTest.bat
wrapper  | イベントコマンド「jvm_restart」: コマンド起動 (pid: 1188) 、15秒間までブロック中...
10秒間のクリーニングアップ中...
全てクリーニングアップ完了。
wrapper  | イベントコマンド「jvm_restart」: 終了コード「0」でコマンド完了
wrapper  | イベントコマンド「jvm_restart」: 終了コード「0」でコマンド完了:  継続中。
wrapper  | JVM起動中...

メール通知

対応バージョン :3.3.0
対応エディション :プロフェッショナル版スタンダード版 (未対応)コミュニティー版 (未対応)
対応プラットフォーム :WindowsMac OSXLinuxIBM AIXFreeBSDHP-UXSolarisIBM z/Linux

フリーズ・イベントに応じた外部コマンドを実行する機能に加えて、 問題発生時にメール通知を送信するように設定することも可能です。 このメールには、シンプルな内容だけでなく、任意の設定次第で、 ログ出力の内容を添付して送信することもできます。

イベント「jvm_unexpected_exit時にメール通知を受信するには、以下のように設定します:

your "wrapper.conf" for the configuration
wrapper.event.default.email.smtp.host=mail.example.com
wrapper.event.default.email.sender=wrapper-app@example.com
wrapper.event.default.email.recipient=sysadmin@example.com
wrapper.event.jvm_unexpected_exit.email=TRUE
wrapper.event.jvm_unexpected_exit.email.subject=あらヤダ!アプリケーションがクラッシュしました!
wrapper.event.jvm_unexpected_exit.email.maillog=ATTACHMENT

これでJVMクラッシュするときには、いつでも次のようなメールが送信されます。

イベント「jvm_unexpected_exit」時のメール内容
Subject: あらヤダ!アプリケーションがクラッシュしました!
To: sysadmin@example.com
From: wrapper-app@example.com

Java Service Wrapper イベント通知

ホスト: myserver
アプリ名: testwrapper
         TestWrapperサンプルアプリケーション

イベント: jvm_unexpected_exit
--

INFO   | jvm 1    | 2012/09/13 16:00:00 | ...
INFO   | jvm 1    | 2012/09/13 16:00:00 | # The crash happened outside the Java Virtual Machine in native code.
INFO   | jvm 1    | 2012/09/13 16:00:00 | # See problematic frame for where to report the bug.
INFO   | jvm 1    | 2012/09/13 16:00:00 | #
ERROR  | wrapper  | 2012/09/13 16:00:00 | JVM が予想外に終了しました。

障害ばかりでなく、良いニュースを受信することもできます。 次の設定では、JVMが起動してアプリケーションが動作を始めると、その通知として2つ目のメールを送信します。 このようにメールを組み合わせると、サーバー状況を把握でき、とても役に立ちます。 これで、緊急対応が必要か、それともこのままディナーをゆっくり楽しるのか、判断するのに役立ちます。

「wrapper.conf」の設定
wrapper.event.jvm_started.email=TRUE
wrapper.event.jvm_started.email.subject=良いニュースです! アプリケーションが元に戻りました
wrapper.event.jvm_started.email.maillog=ATTACHMENT

Wrapperのメール機能やイベントシステムは、とても強力で柔軟に設定が可能です。 さらに詳しくは、「イベント概要」をご覧ください。

サービスリカバリー(回復)

対応バージョン :3.5.5
対応エディション :プロフェッショナル版スタンダード版コミュニティー版 (未対応)
対応プラットフォーム :WindowsMac OSX (未対応)Linux (未対応)IBM AIX (未対応)FreeBSD (未対応)HP-UX (未対応)Solaris (未対応)IBM z/Linux (未対応)

Wrapperは、信頼できるものであり、Javaクラッシュを取り扱う完全な能力を備えています。 Wrapperプロセス自身はとても安定したものであることは証明されており、 このセクションではWrapperプロセス自身のクラッシュからリカバリー(回復)する方法について説明します。

Windowsサービスマネージャーは、Wrapperを含む全てのWindowsサービスの開始、停止、モニタリング(監視)を担っています。 Wrapperクラッシュのイベント時にどのように処理をするのか、Windowsサービスマネージャーにその処理方法を伝えるように設定することが可能です。 [wrapper.ntservice.recovery.<x>]プロパティを使って、 そのリカバリー(回復)機能を設定できます。 プロパティの設定方法は、『wrapper.ntservice.recovery.プロパティの使用例』 をご覧ください。

参照:コンフィギュレーション プロパティ

参照: クラッシュ

Java Service Wrapper では、必要なコンフィギュレーション設定を含んだ完全なパッケージを提供しており、 それを活用することで、皆様の求めるニーズに合った動作を実現させることができます。 上記の例の他に、工夫次第で様々なことが実現可能となりますので、それぞれ個別にプロパティページをご覧ください。