Takashi Nishigaya
Nishigaya's Weblog
Profile
Takashi Nishigaya
Sr. Java Architect
SunJava Consulting
Professional Services Delivery
アーカイブ
« 11月 2009
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
     
       
今日
Click me to subscribe
Search

Java.net
リンク
 
« Custom JMX Client... | メイン | Tips for accessing... »
日曜日 5 13, 2007
Custom JMX Client using JRuby (Part 2)

前回のエントリ(Part 1)では、JRubyを用いてアプリケーションサーバに含まれるJMXサーバに接続し、HTTPリスナMBeanからスレッド数を取得するまでの基本的な流れを示しました。

ここでの目的は、自分が興味のある監視項目を一定間隔で取得するツールに仕上げることですので、前回のサンプルに以下の2つの機能を追加することになります。

  • 興味のあるMBean名と属性名の一覧を定義し、定義された全ての属性値を取得する
  • 1.の処理をinterval秒間隔で定期的に実行する
  • MBean名と属性名の一覧定義には、簡略に定義できるように以下のような2次元配列(MBean:属性=1:nなので、実際には3次元配列)の表現を用いることにします。

    # 監視対象のMBean名と属性の定義
    
    mbeandefs = [
      # [<MBean名>, [<属性1>, <属性2>,...]]
      ["com.sun.appserv:name=http-listener-1," +
       "virtual-server=server,type=http-listener,category=monitor,server=server",
       ["currentthreadcount-count", "currentthreadbusy-count"]
      ]
    ].each { |def| def[0] = ObjectName.new(def[0]) }
    

    上記の例では、Sun Java System Application Server (glassfish)におけるhttp-listener-1のMBeanに対して、プール内のスレッド数("currentthreadcount-count")とその中で実際に処理中のスレッド数("currentthreadbusy-count")を取得することを定義しています。なお、ここではRuby配列に対してeachメソッドを適用し、配列内のMBean名を予めObjectNameに変換しておきます。

    後は、ループを定義して、mbeandefs配列に定義された全ての監視項目についての属性値の取得を、一定間隔毎に実行する定義をすればよいことになります。

    # 各MBeanの属性を取得・表示する
    
    while true
      mbeandefs.each do |mbeandef|
        name, attrs = mbeandef[0], mbeandef[1]
        values = con.getAttributes(name, attrs.to_java(:String))
        values.each do |data|
          print "#{data.value}¥t"
        end
      end
      puts
      sleep interval
    end
    

    getAttributes(ObjectName,String[])メソッドの戻り値はAttributeListオブジェクトですが、JRubyではJavaのCollectionクラスであってもeachメソッドを用いたループが定義できる点が嬉しいところです。なお、Rubyの配列はJavaのString[]に自動変換されないようなので、to_javaメソッドを用いて明示的にString[]に変換しています。

    後は、mbeandefs配列に監視したいMBeanの定義を追加していけばいいのですが、実際にはもう少し汎用性を高める必要があります。

    CompositeDataタイプの属性に対応する

    MBeanには、単純にgetAttribute(ObjectName,String)メソッドで単純な値を返してくれるものだけではありません。たとえば、JVMに標準で含まれるメモリ概要に関するMBean("java.lang:type=Memory")の属性"HeapMemoryUsage"を取得すると、複数のプロパティをもったCompositeDataとなっていることが分かります。

    irb(main):032:0> data = con.getAttribute(
    irb(main):033:1*   ObjectName.new("java.lang:type=Memory"), "HeapMemoryUsage")
    => #<#<Class:01x3228a1>:0x10980e7 @java_object=javax.management.openmbean.Compos
    iteDataSupport(compositeType=javax.management.openmbean.CompositeType(name=java.
    lang.management.MemoryUsage,items=((itemName=committed,itemType=javax.management
    .openmbean.SimpleType(name=java.lang.Long)),(itemName=init,itemType=javax.manage
    ment.openmbean.SimpleType(name=java.lang.Long)),(itemName=max,itemType=javax.man
    agement.openmbean.SimpleType(name=java.lang.Long)),(itemName=used,itemType=javax
    .management.openmbean.SimpleType(name=java.lang.Long)))),contents={committed=415
    66208, init=0, max=518979584, used=33731368})>
    

    このようなCompositeDataオブジェクトに対しては、更にget(String)メソッドを用いてサブプロパティのキーを与えてあげる必要があります。

    irb(main):034:0> data.get("used")
    => 29729624
    

    そこで、監視項目の定義mbeandefs[]の仕様を見直し、CompositeDataの属性値を取得する場合は、属性値とサブプロパティのキーを"."で結合した"A.e"の形式で定義することにします。

    # 監視対象のMBean名と属性の定義
    
    mbeandefs = [
      # [<MBean名>, [<属性1>, <属性2>,...]]
      ["java.lang:type=Memory" ["HeapMemoryUsage.used"]
    
    ].each { |def| def[0] = ObjectName.new(def[0]) }
    

    属性値を取得する時には、属性名に"."が含まれているかどうかで以下のように条件分岐すれば、シンプルな属性とCompositeDataの値をまとめて取得することができます。MBean属性値を取得するループの部分をCompositeDataにも対応できるように改良すると以下のようになります。CompositeDataの場合であってもJavaのようなキャストは必要なく、そのままget(String)メソッドを呼び出せるのは嬉しいところです。

    # 各MBeanの属性を取得・表示する
    
    while true
      mbeandefs.each do |mbeandef|
        name = mbeandef[0]
        # "<属性>.<サブ属性>"を"."で分解し、それぞれattrs[]、subattrs[]にまとめる
        attrs, subattrs = [], []
        mbeandef[1].each do |attrdef|
          attr = attrdef.split(".")
          attrs << attr[0]
          subattrs << attr[1] # "."がなければnil
        end
        # MBeanの属性値をまとめて取得する
        values = con.getAttributes(name, attrs.to_java(:String))
        # 属性値のリストを表示する
        for i in 0..(values.size-1)
          if subattrs[i].nil? then
            print "#{values[i].value}¥t"
          else
            print "#{values[i].value.get(subattrs[i])}¥t"
          end
        end
      end
      puts
      sleep interval
    end
    

    状況によって名前が変わるMBeanに対応する

    MBeanの中には、環境が変わると名前が変わってしまうものがあります。例えば、JVMのメモリ概要が取得できるtype=MemoryPoolなMBeanは、デフォルトでは以下のような名前で取得されます。

    jmx-mem-normal

    もし、JVMでパラレルGCを有効にすると、"Eden Space"と"Survivor Space"は、それぞれ"Par Eden Space"と"Par Survivor Space"という名前に変わります。

    jmx-mem-parallelgc

    また、コンカレントGCを有効にすると、"Tenured Gen"と"Perm Gen"が"CMS Old Gen"と"CMS Perm Gen"に変わります。

    jmx-mem-concgc

    このように環境によって名前が変わるMBeanを監視対象にする場合、監視項目定義であるmbeandefs[]の管理が面倒になります。このような場合は、MBean名の中で変更される可能性のある部分をワイルドカード"*"に置き換え、queryNames(ObjectName,QueryExp)メソッドを用いて実行時に目的のMBeanを見つけ出すようにするとよいでしょう。MemoryPoolを監視対象とする場合は、以下のようなコードになります。

    objnames = con.queryNames(ObjectName.new("java.lang:type=MemoryPool,*"), nil)
    objnames.each do |name|
      mbeandefs << [name, ["Usage.used", "Usage.max"]]
    end
    

    また、アプリケーションを構成する各コンポーネント(Servlet、JSP、EJBなど)を監視対象にしたい場合では、全てのコンポーネントのMBean名をmbeandefs[]に列挙するのは面倒ですし、コンポーネントの名前が変われば、それに合わせてmbeandefs[]もメンテナンスしなければなりませんん。このような場合も同様にワイルドカードが役に立ちます。例えば、Sun Java System Application Server (glassfish)のEJBコンテナ上で動作している全てのEJBについて、EJBプールの使用状態を監視するには、以下のような定義をすればよいことになります。

    objnames = con.queryNames(
      ObjectName.new("com.sun.appserv:type=bean-pool,*"), nil)
    objnames.each do |name|
      mbeandefs << [name, ["numbeansinpool-highwatermark",
                           "numbeansinpool-current"]]
    end
    

    いかがでしょうか。JRubyのようなスクリプト言語がJavaの実行環境で利用できるようになったことにより、多彩なJavaのAPIをスクリプト言語の簡潔なシンタックスを用いてアクセスできるようになりました。これにより、アプリケーション開発をサポートするためのカスタムツールを簡単に作成できるようになったメリットは大変大きいものがあります。JMXカスタムクライアントは、Javaにおけるスクリプト言語サポートを有効に利用する1つの代表的な例であるということができるでしょう。ここで紹介したサンプルのJRubyスクリプトを以下のリンクからダウンロードできるようにしました。

    上記スクリプトはまだまだ改善の余地が沢山ありますので、興味のある方は自由にダウンロードして、ご自身の開発プロジェクト用にカスタマイズして使ってみてください。

    Posted at 01:36午前 5 13, 2007 by Takashi Nishigaya in Java  |  投稿されたコメント[0]

    投稿されたコメント:

    コメント
    • HTML文法 不許可
    « Custom JMX Client... | メイン | Tips for accessing... »