CLOVER🍀

That was when it all began.

Ehcacheの統計情報を、JMXで取得する

キャッシュライブラリのEhcacheの情報を、JMXで取得する方法について。

まあ、基本的にはこのページの通りです。

JMX Management and Monitoring
http://ehcache.org/documentation/operations/jmx

例ではJConsoleを使っていますが、ここではGroovyを使って値を取得します。

まずは、監視される側のプログラム。
ehcache-server.groovy

import java.lang.management.ManagementFactory

@Grab('net.sf.ehcache:ehcache:2.7.2')
import net.sf.ehcache.Cache
import net.sf.ehcache.CacheManager
import net.sf.ehcache.Element
import net.sf.ehcache.management.ManagementService

def cacheManager = CacheManager.create()

def mbeanServer = ManagementFactory.platformMBeanServer
ManagementService.registerMBeans(cacheManager, mbeanServer, false, false, true, true)

def cache = cacheManager.getCache('simpleCache')

(1 .. 10).step(2).each {
    cache.put(new Element("key${it}".toString(), "value${it}".toString()))
}

(1 .. 10).each {
    cache.get("key${it}".toString())
}

while (true) {
    Thread.sleep(3 * 1000L)
}

cacheManager.shutdown()

設定ファイルは、こんな感じです。
ehcache.xml

ehcache.xml 
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
  <diskStore path="java.io.tmpdir"/>
  <defaultCache
     maxEntriesLocalHeap="10000"
     eternal="false"
     timeToIdleSeconds="120"
     timeToLiveSeconds="120"
     maxEntriesLocalDisk="10000000"
     diskExpiryThreadIntervalSeconds="120"
     memoryStoreEvictionPolicy="LRU">
    <persistence strategy="localTempSwap" />
  </defaultCache>

  <cache name="simpleCache"
     maxEntriesLocalHeap="10000"
     eternal="false"
     timeToIdleSeconds="120"
     timeToLiveSeconds="120"
     maxEntriesLocalDisk="10000000"
     diskExpiryThreadIntervalSeconds="120"
     memoryStoreEvictionPolicy="LRU">
    <persistence strategy="localTempSwap" />
  </cache>
</ehcache>

ちなみに、この設定ファイルのcacheの属性に

statistics="true"

みたいなことが書けますが、JMXとは関係ないようです…。

プログラムのポイントは、作成したCacheManagerをManagementService#registerMBeansに登録すること。
自分で。

def cacheManager = CacheManager.create()

def mbeanServer = ManagementFactory.platformMBeanServer
ManagementService.registerMBeans(cacheManager, mbeanServer, false, false, true, true)

registerMBeansの後ろの方に、4つboolean引数が付いていますが、これは順に

  • CacheManagerをMBeanとして登録する
  • CacheをMBeanとして登録する
  • Cacheの設定情報をMBeanとして登録する
  • Cacheの統計情報をMBeanとして登録する

となります。CacheManagerをMBeanに登録すると停止などができたり、Cacheを登録するキャッシュの全クリアができたりしますが、そんなに便利ではなさそうなのでfalseにしています。

ManagementService
http://ehcache.org/apidocs/net/sf/ehcache/management/ManagementService.html

実際、ドキュメントの例でも統計情報しか有効化されていません。

で、Rangeのstepに2を入れて、5つ要素を登録して、その後10つ取得しようとします。

(1 .. 10).step(2).each {
    cache.put(new Element("key${it}".toString(), "value${it}".toString()))
}

(1 .. 10).each {
    cache.get("key${it}".toString())
}

Cacheには、5つしか要素が入っていないので、キャッシュミスが5回発生することになるはずです。

では、このプログラムに浮いててもらいます。

$ export JAVA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9012 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false"
$ groovy ehcache-server.groovy 
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

では、MBeanの情報を取得するプログラム。前回作ったGroovyスクリプトの簡易版です。
ehcache-monitor.groovy

import javax.management.ObjectName
import javax.management.remote.JMXConnectorFactory
import javax.management.remote.JMXServiceURL

def serverUrl = 'service:jmx:rmi:///jndi/rmi://localhost:9012/jmxrmi'

def serviceUrl = new JMXServiceURL(serverUrl)
def connector = JMXConnectorFactory.connect(serviceUrl)

def printMonitor = { mbean ->
    def format = { collection ->
        collection
        .collect { "      Name: [$it]${System.lineSeparator()}" }
        .join('')
    }

    def formatAttributes = { names ->
        names
        .collect { name ->
                "      Name: [$name], " +
                "Value: [${mbean.getProperty(name)}]" +
                "${System.lineSeparator()}" }
        .join('')
    }

    def attributes = formatAttributes(mbean.listAttributeNames())
    def operations = format(mbean.listOperationNames())
    def operationDescriptions = format(mbean.listOperationDescriptions())

    def mbeanInfo = """|[MBean]
                       |  Name: ${mbean.name()}
                       |    Attributes:
                       |${attributes}
                       |    Operations:
                       |${operations}
                       |    OperationDescriptions:
                       |${operationDescriptions}""".stripMargin()
            println(mbeanInfo)
}

def monitor = { server, query ->
    def modules =
        server
        .queryNames(query, null)
        .collect { objectName -> new GroovyMBean(server, objectName) }

    modules.each(printMonitor)
}


try {
    def query = new ObjectName('net.sf.ehcache:*')

    def server = connector.MBeanServerConnection
    monitor(server,
            new ObjectName('net.sf.ehcache:type=CacheStatistics,*'))
    monitor(server,
            new ObjectName('net.sf.ehcache:type=CacheConfiguration,*'))
} finally {
    connector.close()
}

クエリの範囲は、ちょっと絞っています。

    monitor(server,
            new ObjectName('net.sf.ehcache:type=CacheStatistics,*'))
    monitor(server,
            new ObjectName('net.sf.ehcache:type=CacheConfiguration,*'))

実行。

$ groovy ehcache-monitor.groovy

出力結果。まずは、統計情報から。

[MBean]
  Name: net.sf.ehcache:type=CacheStatistics,CacheManager=__DEFAULT__,name=simpleCache
    Attributes:
      Name: [AssociatedCacheName], Value: [simpleCache]
      Name: [WriterQueueLength], Value: [0]
      Name: [OnDiskHits], Value: [0]
      Name: [CacheMisses], Value: [5]
      Name: [OffHeapHits], Value: [0]
      Name: [InMemoryHits], Value: [5]
      Name: [CacheHits], Value: [5]
      Name: [OnDiskMisses], Value: [5]
      Name: [ObjectCount], Value: [5]
      Name: [WriterMaxQueueSize], Value: [0]
      Name: [MemoryStoreObjectCount], Value: [5]
      Name: [OffHeapStoreObjectCount], Value: [0]
      Name: [DiskStoreObjectCount], Value: [5]
      Name: [CacheHitPercentage], Value: [0.5]
      Name: [CacheMissPercentage], Value: [0.5]
      Name: [InMemoryHitPercentage], Value: [0.5]
      Name: [OffHeapHitPercentage], Value: [0.0]
      Name: [OnDiskHitPercentage], Value: [0.0]
      Name: [InMemoryMisses], Value: [5]
      Name: [OffHeapMisses], Value: [0]

    Operations:

    OperationDescriptions:

登録されているオブジェクトが5個であることや、キャッシュヒット率やキャッシュミス率が確認できます。

その他、設定情報。

[MBean]
  Name: net.sf.ehcache:type=CacheConfiguration,CacheManager=__DEFAULT__,name=simpleCache
    Attributes:
      Name: [Name], Value: [simpleCache]
      Name: [Eternal], Value: [false]
      Name: [TerracottaClustered], Value: [false]
      Name: [MemoryStoreEvictionPolicy], Value: [LRU]
      Name: [MaxBytesLocalHeap], Value: [0]
      Name: [MaxBytesLocalDisk], Value: [0]
      Name: [OverflowToOffHeap], Value: [false]
      Name: [OverflowToDisk], Value: [true]
      Name: [TimeToIdleSeconds], Value: [120]
      Name: [TimeToLiveSeconds], Value: [120]
      Name: [MaxEntriesLocalHeap], Value: [10000]
      Name: [MaxEntriesLocalDisk], Value: [10000000]
      Name: [DiskExpiryThreadIntervalSeconds], Value: [120]
      Name: [MaxBytesLocalOffHeap], Value: [0]
      Name: [DiskPersistent], Value: [false]
      Name: [MaxElementsInMemory], Value: [10000]
      Name: [DiskSpoolBufferSizeMB], Value: [30]
      Name: [MaxElementsOnDisk], Value: [10000000]
      Name: [MaxMemoryOffHeapInBytes], Value: [0]
      Name: [TerracottaConsistency], Value: [na]
      Name: [LoggingEnabled], Value: [false]

    Operations:

    OperationDescriptions: