TomcatでJMXを使ってみる
JMX(Java Management eXtensions)はJ2SE 5.0で取り入れられた新機能で、外部からJVMの監視・管理を行うことが出来る仕組み。
今やってるプロジェクトでは独自のDBコネクションプーリングクラスを使ってるので、コネクションの使用状況などを監視できればいいなぁというのがそもそものきっかけ。
JMX自体についての詳しい説明はGoogleで検索すれば情報は出て来ると思うので、ここでは具体的な使い方とかをメモ程度に。とは言え基本的な事を全く書かないのも何なのでちょっとだけ。
○JMXの仕組み(超簡単に)
その名の通りJavaを管理(manage)するための仕組みで、管理対象のJavaオブジェクト(今回の例ではコネクションプーリングクラス)に対してMBeanという管理用のオブジェクトを作り、クライアント(※1)はMBeanサーバーを経由でMBeanにアクセスして、管理対象のオブジェクトを管理する。
クライアント → MBeanサーバー → MBean → 管理対象のJavaオブジェクト
という感じ。このページの図2が分かりやすかった
新たに作る必要があるのはMBean(といくつかの設定変更)のみ。MBeanサーバーは独自のを作ることも可能だけど、普通は「プラットフォーム MBean サーバー」と言うJVMに組み込まれたものを使用する。
あと当たり前と言えば当たり前だけど、MBeanは作成しただけじゃダメで、MBeanサーバーに登録する必要がある。
(※1)クライアントにあたるのは一般的には管理・監視ソフトで、JMXの仕組みに沿ったものなら何でもok。
○環境
Tomcat 5.5.23
J2SE 1.5.0
Windows XP SP2
○Tomcatの設定
・JMXを有効に
起動時のオプションでJMXを有効にするため、以下のオプションを追加する。
-Dcom.sun.management.jmxremote
その他関連するオプションについてはこの辺りを参考に。
・Tomcatの管理画面へアクセスできるユーザーを設定(必須ではないけど)
CATALINA_HOME/conf/tomcat-users.xml
<tomcat-users> <role rolename="manager"/> <user username="admin" password="foobar" roles="manager"/> </tomcat-users>
○MBeanの作成
MBeanにはいくつか種類があるけど、今回は一番手っ取り早い標準MBeanというのを作成。
・MBeansのインターフェースを作成
今回は現在使用されているコネクション数を返すメソッドをとりあえず作ってみる。特に難しいことはない。
package tv.kazu.test.mbeans public interface DBStatusMBean { public int getUsedConnections(); }
・MBeansのインターフェースを実装したクラスを作成
これも特別変わったことはしてない
package tv.kazu.test.mbeans public class DBStatus { public DBStatus() { } public int getUsedConnections() { // MYDBConnPoolはどっかで実装されている事とする return MyDBConnPool.getUsedConnections(); } }
全然難しいことはない。
○MBeanの登録
・登録用ユーティリティクラスを作成
前述の通り、MBeanはMBeanサーバーに登録する必要がある。少しだけ難しくなるけど、大したこと無い。
MBeanのコンストラクタとかで自分自身を登録してもいいし、別のクラスから(複数のMBeanがある場合には一括)登録してもいいけど、いずれにしてもユーティリティクラスがあると便利。
ちょっと端折ったけどこんなクラスを作成した。例外処理を除くとスゴいシンプルってのが分かると思う。
public class JMXUtils { private static MBeanServer mbeanServer = null; static public void registerAll() { mbeanServer = getMbeanServer(); //複数のMBeanがある場合はここで全て登録 DBStatus bean = new DBStatus(); registerMBean(bean, "tv.kazu.test.mbean:type=DBStatus"); } //MBeanの登録 static public void registerMBean(Object mbean, String nameStr) { mbeanServer = getMbeanServer(); ObjectName objectName = null; try { objectName = new ObjectName(nameStr); } catch (MalformedObjectNameException e) { e.printStackTrace(); } catch (NullPointerException e) { e.printStackTrace(); } try { mbeanServer.registerMBean(mbean, objectName); } catch (InstanceAlreadyExistsException e) { e.printStackTrace(); } catch (MBeanRegistrationException e) { e.printStackTrace(); } catch (NotCompliantMBeanException e) { e.printStackTrace(); } } //MBeanサーバーの取得 static public MBeanServer getMbeanServer() { if (mbeanServer == null) { mbeanServer = ManagementFactory.getPlatformMBeanServer(); } return mbeanServer; } }
JMXUtils.registerAll()を呼べばMBeanが登録されるのが分かったと思うけど、じゃそれをどこで呼び出すかというのが次の話。
・ContextListnerを実装
詳細は省くけど、ContextListnerってのを作ってちゃんと設定しておくとTomcatの起動時にそれが呼ばれるのでそこでMBeanの登録をする(すごい適当な説明…)。
public final class ContextListener implements ServletContextListener { public void contextDestroyed(ServletContextEvent arg0) { } public void contextInitialized(ServletContextEvent arg0) { try { System.out.println("ContextListnerが呼ばれてます。"); JMXUtils.registerAll(); } catch (IOException e) { e.printStackTrace(); } } }
あとはweb.xmlにContextListenerのエントリを記述。
<listener> <listener-class>tv.kazu.test.jmx.ContextListener</listener-class> </listener>
○最後にTomcatの設定をもうちょい
・mbeans-descriptor.xml
後はTomcatに対して、「こんなMBeanを使いますよ」と伝えてあげればいい。その設定にはmbeans-descriptor.xmlというファイルを作成する。
<?xml version="1.0"?> <mbeans-descriptors> <mbean name="DBStatus" className="org.apache.catalina.mbeans.ClassNameMBean" description="DB status" domain="Catalina" type="tv.kazu.test.mbean.DBStatus"> <attribute name="className" description="Fully qualified class name of the managed object" type="java.lang.String" writeable="false"/> <attribute name="debug" description="The debugging detail level for this component" type="int"/> <operation name="getUsedConnections" description="Returns the number of the connections." impact="ACTION" return="int"> </operation> </mbean> </mbeans-descriptors>
・デプロイ
MBeanインターフェースクラスとmbeans-descriptor.xmlをJARファイルにして
CATALINA_HOMEcommonlib
に置くか、テスト用途とかでJARをいちいち作るのが面倒な場合はファイルを個別に
CATALINA_HOMEcommonclasses
に置く。
mbeans-descriptor.xmlはルート(CATALINA_HOMEcommonclasses)に、クラスファイルはパッケージの階層構造のままコピー。今回の例では以下のフォルダに。
CATALINA_HOMEcommonclassestvkazutestmbean
・server.xmlの修正
server.xmlにmbeans-descriptor.xmlに関する記述を追加。
以下の行を
<Listener className="org.apache.catalina.mbeans.ServerLifecycleListner" />
このように変更
<Listener className="org.apache.catalina.mbeans.ServerLifecycleListner" debug="0" descriptors="/mbeans-descriptor.xml" />
後はTomcatを再起動。
○動作確認
動作確認にはjconsoleとかを使う。詳細はまたの機会に。
○参考にしたサイト
・英語だけどこのサイトが参考になった
・mbean-descriptor.xmlに関してはTomcatのページを参照
・ITProにも記事があった。
・J2SE5.0虎の穴。この人のページにはよくお世話になる。
・ちょっと取っつきにくいけど、Sunのサイトはやっぱり基本。色々あるけどこんなページとかから辿れば色々情報が得られるかも。