JMX

Jolokiaを使ってTomcatのJMXメトリクスを取得

Tomcatのパフォーマンスについて調べる事になると必ず必要になってくる、「JMXメトリクスを取得できる仕組み」を用意しておきます。TomcatのJMXメトリクスを取得する方法はいくつかありますが、今回はJolokiaを使う方法で行います。Jolokiaを使うとHTTPベース(curlコマンド)でJMXメトリクスを取得できるためです。Jolokiaを利用するにあたっても、JAR形式とWAR形式を利用する2パターンがあります。今回はTomcatが動作するJVMにアタッチする(-javaagentオプション)方法で行います。WAR形式の場合TomcatへデプロイするだけでJMXメトリクスが取得できるようになってお手軽なのですが、JAR形式のほうがJVMの低レベルのメトリクスまで取得可能という事らしいです。(まだそれを実感できるまで使い込めてはいないですが、、)今回WARタイプを選択しなかった理由は、純粋にTomcatにデプロイしているアプリケーションの動作を観察したいためです。WARタイプを使うとJolokiaエージェント自体もTomcatアプリケーションのJMXメトリクスに含まれてしまいます。

以下のダウンロードサイトから、JVM-Agent版をダウンロードしてください。私がダウンロードしたタイミングのバージョンは「jolokia-jvm-2.2.9-javaagent.jar」でした。

https://jolokia.org/download.html

これを適当なところに配備してください。私は/opt/jolokia/配下にjolokia-agent.jarという名前で配備しました。

JMXの有効化

rootで以下実施します。デフォルトでsetenv.shは存在していませんが、$CATALINA_HOME/binの配下に作成しておけばtomcat起動時に、catalina.shの中から起動がかかるようになっています。

touch $CATALINA_HOME/bin/setenv.sh
chmod +x $CATALINA_HOME/bin/setenv.sh
chown tomcat:tomcat $CATALINA_HOME/bin/setenv.sh
vi $CATALINA_HOME/bin/setenv.sh

以下内容(JVMオプション)を記載し、JVMにJolokiaのエージェントを組み込みます。

export CATALINA_OPTS="$CATALINA_OPTS \
-javaagent:/opt/jolokia/jolokia-agent.jar=port=8778,host=127.0.0.1"

port 8778がリスニングポイントで、host=127.0.0.1とする事でローカルからの接続のみ可能となります。

Tomcatを再起動して確認する

[sooni@vm105 ~]$ sudo systemctl restart tomcat
[sudo] sooni のパスワード:
[sooni@vm105 ~]$
-- リスニングポート確認
[sooni@vm105 ~]$ netstat -tulnp | grep 8778
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp6       0      0 127.0.0.1:8778          :::*                    LISTEN      -
[sooni@vm105 ~]$

実際にcurlをたたいてメトリクスを取得

curl -s -X POST  http://localhost:8778/jolokia/ -H 'Content-Type: application/json' -d '
[
  { "type": "read", "mbean": "java.lang:type=Memory", "attribute": "HeapMemoryUsage" },
  { "type": "read", "mbean": "java.lang:type=MemoryPool,name=Metaspace", "attribute": "Usage" },
  { "type": "read", "mbean": "java.lang:type=Threading", "attribute": "ThreadCount" },
  { "type": "read", "mbean": "Catalina:name=\"http-nio-8080\",type=ThreadPool", "attribute": "currentThreadCount" },
  { "type": "read", "mbean": "Catalina:name=\"http-nio-8080\",type=ThreadPool", "attribute": "currentThreadsBusy" },
  { "type": "read", "mbean": "Catalina:name=\"http-nio-8080\",type=ThreadPool", "attribute": "connectionCount" },
  { "type": "read", "mbean": "Catalina:name=\"http-nio-8080\",type=GlobalRequestProcessor", "attribute": "requestCount" },
  { "type": "read", "mbean": "Catalina:name=\"http-nio-8080\",type=GlobalRequestProcessor", "attribute": "processingTime" },
  { "type": "read", "mbean": "Catalina:name=\"http-nio-8080\",type=GlobalRequestProcessor", "attribute": "errorCount" },
  { "type": "read", "mbean": "Catalina:name=\"http-nio-8080\",type=GlobalRequestProcessor", "attribute": "bytesReceived" },
  { "type": "read", "mbean": "java.lang:name=PS Scavenge,type=GarbageCollector", "attribute": "CollectionCount" },
  { "type": "read", "mbean": "java.lang:name=PS MarkSweep,type=GarbageCollector", "attribute": "CollectionCount" }
]' | jq
以下解説
--ヒープメモリの使用状況を取得
{ "type": "read", "mbean": "java.lang:type=Memory", "attribute": "HeapMemoryUsage" }

--取得例
  {
    "request": {
      "mbean": "java.lang:type=Memory",
      "attribute": "HeapMemoryUsage",
      "type": "read"
    },
    "value": {
      "init": 536870912,
      "committed": 514850816,
      "max": 954728448,
      "used": 49032976
    },
    "status": 200,
    "timestamp": 1746792116
  }
--Metaspace のメモリ使用状況 を取得
{ "type": "read", "mbean": "java.lang:type=MemoryPool,name=Metaspace", "attribute": "Usage" }

--取得例
  {
    "request": {
      "mbean": "java.lang:name=Metaspace,type=MemoryPool",
      "attribute": "Usage",
      "type": "read"
    },
    "value": {
      "init": 0,
      "committed": 21299200,
      "max": -1,            -- 無制限
      "used": 20767008
    },
    "status": 200,
    "timestamp": 1746780166
  }
--JVM全体アクティブなスレッド数 を取得
{ "type": "read", "mbean": "java.lang:type=Threading", "attribute": "ThreadCount" }

--取得例
{
  "request": {
    "mbean": "java.lang:type=Threading",
    "attribute": "ThreadCount",
    "type": "read"
  },
  "value": 25,   -- アクティブなスレッド数
  "status": 200,
  "timestamp": 1746760832
}
-- Tomcat のスレッドプールスレッド総数 を取得
-- 負荷が上がればmaxThreadsまでは増えるが減る事はない。(Tomcat再起動でリセット)
--
{ "type": "read", "mbean": "Catalina:name=\"http-nio-8080\",type=ThreadPool", "attribute": "currentThreadCount" }

--取得例
  {
    "request": {
      "mbean": "Catalina:name=\"http-nio-8080\",type=ThreadPool",
      "attribute": "currentThreadCount",
      "type": "read"
    },
    "value": 10,
    "status": 200,
    "timestamp": 1746780166
  }
-- Tomcat のスレッドプール内で実際にリクエストを処理しているスレッドの数 を取得
-- リクエストが殺到すると増加し、処理完了後は減少。動作していない状況においては0となる

{ "type": "read", "mbean": "Catalina:name=\"http-nio-8080\",type=ThreadPool", "attribute": "currentThreadsBusy" }

--取得例
  {
    "request": {
      "mbean": "Catalina:name=\"http-nio-8080\",type=ThreadPool",
      "attribute": "currentThreadsBusy",
      "type": "read"
    },
    "value": 1,
    "status": 200,
    "timestamp": 1746775370
  }
--Tomcat がうけつけているリクエスト数 を取得
-- 瞬間値。currentThreadsBusyとほぼ同じ値になる。Keep-Aliveが利用された場合は
-- 違いが出てきそう、、

{ "type": "read", "mbean": "Catalina:name=\"http-nio-8080\",type=ThreadPool", "attribute": "connectionCount" }

--取得例
  {
    "request": {
      "mbean": "Catalina:name=\"http-nio-8080\",type=ThreadPool",
      "attribute": "connectionCount",
      "type": "read"
    },
    "value": 1,
    "status": 200,
    "timestamp": 1746779017
  }
-- Tomcat の HTTP コネクタ (http-nio-8080) が処理したリクエストの総数 を取得
-- Tomcatが立ち上がっている限り減る事は無い。再起動で0クリアされる。

{ "type": "read", "mbean": "Catalina:name=\"http-nio-8080\",type=GlobalRequestProcessor", "attribute": "requestCount" }

--取得例
  {
    "request": {
      "mbean": "Catalina:name=\"http-nio-8080\",type=GlobalRequestProcessor",
      "attribute": "requestCount",
      "type": "read"
    },
    "value": 28,
    "status": 200,
    "timestamp": 1746775939
  }
-- Tomcatが各リクエストにどれくらい時間を使っているかの累積時間(ミリ秒) を取得
-- Tomcatが立ち上がっている限り減る事は無い。再起動で0クリアされる。

{ "type": "read", "mbean": "Catalina:name=\"http-nio-8080\",type=GlobalRequestProcessor", "attribute": "processingTime" }

--取得例
  {
    "request": {
      "mbean": "Catalina:name=\"http-nio-8080\",type=GlobalRequestProcessor",
      "attribute": "processingTime",
      "type": "read"
    },
    "value": 4837,
    "status": 200,
    "timestamp": 1746776109
  }
-- HTTPリクエストで発生したエラーの累積総数 を取得
-- Tomcatが立ち上がっている限り減る事は無い。再起動で0クリアされる。

{ "type": "read", "mbean": "Catalina:name=\"http-nio-8080\",type=GlobalRequestProcessor", "attribute": "errorCount" }

--取得例
  {
    "request": {
      "mbean": "Catalina:name=\"http-nio-8080\",type=GlobalRequestProcessor",
      "attribute": "errorCount",
      "type": "read"
    },
    "value": 0,
    "status": 200,
    "timestamp": 1746776410
  }
-- Tomcat起動時から現在までに受信したリクエストのデータ量(累積値)
-- HTTPリクエストのヘッダー・ボディ・パラメータすべてが含まれる
-- Tomcatが立ち上がっている限り減る事は無い。再起動で0クリアされる。

{ "type": "read", "mbean": "Catalina:name=\"http-nio-8080\",type=GlobalRequestProcessor", "attribute": "bytesReceived" }

--取得例
  {
    "request": {
      "mbean": "Catalina:name=\"http-nio-8080\",type=GlobalRequestProcessor",
      "attribute": "bytesReceived",
      "type": "read"
    },
    "value": 16643,
    "status": 200,
    "timestamp": 1746776744
  }
GCに関するMBean名はGCアルゴリズムによって複数あります

以下curlで対象JVMのGCアルゴリズムがわかります。

curl -s http://localhost:8778/jolokia/search/java.lang:name=*,type=GarbageCollector | jq
Parallel GC用
--JVM の Young GC (PS Scavenge) の実行回数 を取得
{ "type": "read", "mbean": "java.lang:name=PS Scavenge,type=GarbageCollector", "attribute": "CollectionCount" }

--取得例
{
  "request": {
    "mbean": "java.lang:name=PS Scavenge,type=GarbageCollector",
    "attribute": "CollectionCount",
    "type": "read"
  },
  "value": 10,
  "status": 200,
  "timestamp": 1746760832
}
--JVM の Old GC (PS MarkSweep) (フルGC)の実行回数 を取得
{ "type": "read", "mbean": "java.lang:name=PS MarkSweep,type=GarbageCollector", "attribute": "CollectionCount" }

--取得例
{
  "request": {
    "mbean": "java.lang:name=PS MarkSweep,type=GarbageCollector",
    "attribute": "CollectionCount",
    "type": "read"
  },
  "value": 3,
  "status": 200,
  "timestamp": 1746760832
}
G1 GC用

以下MBean名を利用すると取得できます。

"java.lang:name=G1 Young Generation,type=GarbageCollector"
"java.lang:name=G1 Old Generation,type=GarbageCollector"
"java.lang:name=G1 Concurrent GC,type=GarbageCollector"

Fluent-Bitで収集してElasticsearchへ連携

おまけ

JMXのメトリクスを取得するにはRMIを使う方法もあります。その場合は以下を$CATALINA_HOME/bin/setenv.shへ追記してください。

RMIを使う場合

export CATALINA_OPTS="$CATALINA_OPTS \
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=9999 \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.authenticate=false"

スポンサーリンク
タイトルとURLをコピーしました