Remote Recording Stream

原文はこちら。
The original article was written by Erik Gahlin (Consulting Member of Technical Staff, Oracle).
https://egahlin.github.io/2021/05/17/remote-recording-stream.html

アプリケーション監視ツールは、以前からJMXを使用してネットワーク経由で継続的にデータを取得することができました。例えば、CPU負荷はOperatingSystemMXBeanから取得し、JDK Mission Controlで視覚化できます。JFRは、スタックトレースやタイムスタンプ付きの値など、構造化されたよりリッチなデータを提供しますが、JDK 16までは、これらの情報を発生時にネットワーク経由で転送する方法がありませんでした。

JDK 14で、ストリームイベントへのAPIのサポートが追加されました。コードスニペットでご紹介しましょう。

JEP 349: JFR Event Streaming
https://openjdk.java.net/jeps/349

1. Passive(プロセス内)

try (EventStream stream = EventStream.openRepository()) {
    stream.onEvent("jdk.JavaMonitorEnter", System.out::println),
    stream.start();
}

2. Passive(プロセス外)

Path path = Path.of("/repository/2021_05_16_09_48_31_60185");
try (EventStream stream = EventStream.openRepository(path) {
    stream.onEvent("jdk.JavaMonitorEnter", System.out::println),
    stream.start();
}

3. Active(プロセス内)

try (RecordingStream stream = new RecordingStream()) {
    stream.enable("jdk.JavaMonitorEnter").withStackTrace();
    stream.onEvent("jdk.JavaMonitorEnter", System.out::println),
    stream.start();
}

ここでいうアクティブとは、3つ目のコードスニペットのenabledメソッドに見られるように、録画のライフサイクルやイベントの設定をストリームが制御できることを意味します。上記のAPIは多くのシナリオに対応していますが、以下のような場合には使えません。

  • リモートホスト上のJavaプロセスを監視する
  • リモートホストや別のプロセスで記録されている内容を制御する

JDK 11から、リモートで録画をコントロールしたりダウンロードしたりできるFlightRecordingMXBeanがOpenJDKに存在します。

Interface FlightRecorderMXBean
https://docs.oracle.com/en/java/javase/16/docs/api/jdk.management.jfr/jdk/management/jfr/FlightRecorderMXBean.html

これでJDK Mission Control が録画データを取得し、リモート・マシンでイベントを構成します。JDK 15およびそれ以前のリリースでは、クライアントがデータを読み取る前に録画を停止する必要がありました。

JDK Mission Control
https://www.oracle.com/java/technologies/jdk-mission-control.html
https://www.oracle.com/jp/java/technologies/jdk-mission-control.html

JDK 16ではこの制限がなくなり、JFRでMBeanServerConnectionを使用してリモートホストを監視できるようになりました。

Interface MBeanServerConnection
https://docs.oracle.com/en/java/javase/16/docs/api/java.management/javax/management/MBeanServerConnection.html

4. Active(プロセス外およびネットワーク越し)

String host = "com.example";
int port = 7091;
 
String url = "service:jmx:rmi:///jndi/rmi://" + host + ":" + port + "/jmxrmi";
 
JMXServiceURL u = new JMXServiceURL(url);
JMXConnector c = JMXConnectorFactory.connect(u);
MBeanServerConnection connection = c.getMBeanServerConnection();

try (RemoteRecordingStream stream = new RemoteRecordingStream(connection)) {
    stream.enabled("jdk.JavaMonitorEnter").withStackTrace();
    stream.onEvent("jdk.JavaMonitorEnter", System.out::println),
    stream.start();
}

RemoteRecordingStreamの実装では、FlightRecorderMXBean::readStream(long)メソッドからデータを読み取り、リモートホスト上でJVMが行うのと同様に、チャンク単位でローカルにディスクに書き込みます。その後、別のスレッドがディスク上のデータを解析し、onEventハンドラにイベントをディスパッチします。これで1秒に1回新しいデータが読めるようになります。

readStreamメソッド(Interface FlightRecorderMXBean)
https://docs.oracle.com/en/java/javase/16/docs/api/jdk.management.jfr/jdk/management/jfr/FlightRecorderMXBean.html#readStream(long)

データセグメントが完了する前にパーサースレッドがデータを読まないようにするため、チャンクヘッダーには、ファイルのどの範囲までデータを読めるかを示すサイズフィールドがあります。新しいデータが到着してセグメントが完了すると、このフィールドが更新されます。読み込み中にサイズフィールドが変更されないようにするために、パーサーがワードティアリング(word tearing)を避けるためのプロトコルがあります。

Remote Streaming Overview

チャンクファイルが読み込まれ、そのイベントをディスパッチすると、ファイルはクライアントから削除されます。代わりにデータを保持するには、setMaxAge(Duration)setMaxSize(long)の2つのポリシーを設定して、データを保持する期間を決定できます。

RemoteRecordingStreamクラスは、イベントのストリーミングだけではなく、ディスクリポジトリを別のホストに移行することもできます。監視対象のアプリケーションがクラッシュして、ホスト上のディスクリポジトリファイルが削除されても、RemoteRecordingStreamが動作するマシン上ではまだ利用可能です。今後、RemoteRecordingStreamdumpメソッドを追加し、何か問題が発生した場合に簡単にファイルを抽出できるようにする予定です。

Streaming event metadata

他のプロセスからイベントをストリーミングする際の複雑な点は、イベントのメタデータにアクセスできないことです。インプロセスでのストリーミングにおいて、FlightRecorder::getEventTypes()メソッドを呼び出して、登録されたすべてのイベントタイプのリストを取得できます。

getEventTypesメソッド(Class FlightRecorder)
https://docs.oracle.com/en/java/javase/16/docs/api/jdk.jfr/jdk/jfr/FlightRecorder.html#getEventTypes()

イベントタイプの知識がないと、フィールド・レイアウトや、どのイベントを有効にしたり設定したりするかを決定できません。

このような状況を改善するために、EventStreamインターフェースに新しいメソッドが追加されました。

Interface EventStream
https://docs.oracle.com/en/java/javase/16/docs/api/jdk.jfr/jdk/jfr/consumer/EventStream.html

void onMetadata(MetadataEvent);

MetadataEventは、登録されたすべてのイベントタイプとのリストと、JDKに付属するdefaultprofileの2つの構成を持ちます。onEventハンドラが呼び出される前にMetadataEventが送信されます。新しいイベントタイプが登録されたり登録解除されたりすると、MetadataEventが更新され、更新されたイベントが送信されます。

Class MetadataEvent
https://docs.oracle.com/en/java/javase/16/docs/api/jdk.jfr/jdk/jfr/consumer/MetadataEvent.html

RemoteRecordingStreamクラスの動作を確認するために、イベントをサブスクライブして標準出力に出すHealthReport.javaという小さな1個のファイルのプログラムがあります。

Class RemoteRecordingStream
https://docs.oracle.com/en/java/javase/16/docs/api/jdk.management.jfr/jdk/management/jfr/RemoteRecordingStream.html
Health Report
https://github.com/flight-recorder/health-report

Health Report

Usage:

$ java HealthReport.java com.example:7091

Resources

JEP 328: Flight Recorder
https://openjdk.java.net/jeps/328
JEP 349: JFR Event Streaming
https://openjdk.java.net/jeps/349
[JDK-8253898] JFR: Remote Recording Stream
https://bugs.openjdk.java.net/browse/JDK-8253898
RemoteRecordingStreamクラス
https://docs.oracle.com/en/java/javase/16/docs/api/jdk.management.jfr/jdk/management/jfr/RemoteRecordingStream.html
MetadataEventクラス
https://docs.oracle.com/en/java/javase/16/docs/api/jdk.jfr/jdk/jfr/consumer/MetadataEvent.html
Health Report
https://github.com/flight-recorder/health-report

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中