New Relic for Java (エージェント バージョン 3.37 以上) には、非同期アクティビティを計測するための API が含まれています。 サポートされているフレームワーク では、エージェントは通常、非同期作業を自動的に計測します。しかし、非同期APIは、詳細を追加するのに便利です。このドキュメントでは、トークンとセグメントを使ってアプリを計測する例を紹介します。
非同期のトラッキングツール。トークンとセグメント JavaエージェントAPIは、非同期アクティビティをトレースする2つの方法を提供しています。
トークン : トークンは、非同期の作業単位を特定のトランザクションに結びつけるために、スレッド間で渡されます。トークンは直接タイミングを取ることはありません。セグメント : セグメントは、非同期アプリケーションコードの任意の部分を測定するために使用されますが、必ずしもメソッドやスレッドに関連付けられているわけではありません。セグメントは通常、コールバック・メカニズムによって完了する外部呼び出しを追跡するために使用されます。 トークン。非同期スレッドの接続 トークンを使用して、別々のスレッド上にある任意の作業単位をリンクします。このセクションでは、トークン関連のコールをまとめて使用し、非同期作業をインストルメント化する方法を説明します。クラスやメソッドの詳細については、Javadocを参照してください。
トークンを使用するには、最初にトークンを作成してから、別の呼び出しを元のトランザクションにリンクする必要があります。他の呼び出し内でできるだけ早くトークンをリンクする必要があります。トークンをすぐにリンクしないと、リンクしようとしている呼び出しの下に@Trace
を含むメソッドが失われるリスクがあります。元の呼び出しでトークンを期限切れにすることもできます。次に、JavaエージェントはNewRelicUIで作業をリンクします。これらの例は、トークン関連の呼び出しを一緒に使用する方法を示しています。
1.トランザクションを開始してから、トークンを作成して期限切れにする 以下のコードスニペットのメソッドparallelStream()
について考えてみます。requestItemAsync()
への呼び出しの一部は別のスレッドで発生するため、トークンが作成されて渡され、その非同期作業を要求元のスレッドにリンクします。
* Example showing multi-threaded implementation of requesting data using a parallel {@link Stream}.
@RequestMapping("parallel_stream")
@Trace(dispatcher = true) // starts a transaction
public ResponseEntity<String> parallelStream(@RequestParam("ids") List<Long> ids) {
final Token token = NewRelic.getAgent().getTransaction().getToken();
.map(id -> requestItemAsync(id, token)) // requestItemAsync represents an example of work being passed to another thread. The token is passed along to allow linking the work on the new thread back to the thread that the token was originally created on.
.filter(item -> item != null)
.collect(Collectors.toList());
return formattedResponse("parallel_stream", results);
本サンプルでのエージェントAPIコールは
@Trace(dispatcher = true)
:エージェントにトランザクションを開始するように指示します。このメソッドの詳細については、 Javadoc を参照してください。getToken()
:作業をリンクするトークンを作成します。このメソッドの詳細については、 Javadoc を参照してください。token.expire()
:トークンを期限切れにします。これにより、トランザクションを終了できます。このメソッドの詳細については、 Javadoc を参照してください。2.トランザクションを非同期としてマークし、リクエストスレッドにリンクする 次のコード例はrequestItemAsync
を示しています。これは、要求元のスレッドとは別のスレッドで実行される可能性があります。このため、前のコード例で作成されたトークンは、 requestItemAsync
のトランザクションにリンクされています。requestItemAsync()
には@Trace(async=true)
アノテーションが付いていることに注意してください。これは、既存のトランザクションにリンクされている場合にこのメソッドをトレースするようにエージェントに指示します。
parallelStream()
がすべての結果を収集した後、トークンは期限切れになります。これは、 parallelStream()
の完了後にトランザクションが開いたままにならないようにするために重要です。
private Item requestItemAsync(long id, Token token) {
本サンプルでのエージェントAPIコールは
@Trace(async = true)
:トランザクションを開始します。このメソッドの詳細については、 Javadoc を参照してください。token.link()
: requestItemAsync()
で実行されている作業(別のスレッドで実行されている)を要求元のスレッドにリンクします。このメソッドの詳細については、Javadoc を参照してください。3.New RelicのUIで非同期トレースを表示する トランザクション追跡の詳細を表示するには、 one.newrelic.com APM & services > (アプリを選択) > Transactions > Transaction trace > Trace details に移動します。
非同期アクティビティは、時間ディメンションで水平方向に重なり合うセグメントによってトレース ウォーターフォール ビューに表示されます。
同じスレッドにあるメソッドをリンクする必要はありませんが、リンクしても悪影響はありません。parallelStream()
の例のように、単一のトークンを共有できる場合がよくあります。
ヒント デフォルトでは、トランザクションは最大3000のトークンを作成でき、各トークンのデフォルトのタイムアウトは180秒です。前者の制限はtoken_limit
configオプションで変更でき、後者はtoken_timeout
configオプションで変更できます。token_limit
を超えるトランザクションのトレースには、 token_clamp
属性が含まれます。いずれかの構成オプションを増やすと、エージェントのメモリ使用量が増える可能性があります。
セグメンテーションです。任意の時間の非同期アクティビティ セグメントは、非同期アプリケーションコードの任意の部分を計測するために使用され、必ずしもメソッドやスレッドに関連付けられている必要はありません。これは、外部サービスへの接続時間を計測するために最もよく使われます。必要に応じてセグメントを使用してください。
コールバックで完了するタイムコード 多くのメソッドにまたがる非同期呼び出しの時間 ワークが作成されてから実行されるまでの時間を計測する(例:スレッドプールの場合) 1.トランザクションの作成と外部サービスの呼び出し 以下のメソッドは、メソッドstoreItem()
を使用して外部サービス(この場合はデータベース)を呼び出します。
* Example showing single threaded implementation of inserting data into a datasource.
@RequestMapping("insert")
@Trace(dispatcher = true) //starts a transaction
public ResponseEntity insert(@RequestParam("id") Long id) {
return new ResponseEntity<>("insert", HttpStatus.OK);
return new ResponseEntity<>("insert", HttpStatus.BAD_REQUEST);
この場合の目標は、 storeItem()
の実行時間を決定するのではなく、LambdaステートメントのCallable
が実行前にスレッドプールで待機している時間を調べることです。このため、トークンの代わりにセグメントが使用され、トークンの場合のように@Trace(async = true)
は必要ありません。
このサンプルでのエージェントのAPIコールは
@Trace(dispatcher = true)
:トランザクションを開始します。このメソッドの詳細については、 Javadoc を参照してください。2.セグメントの開始、外部からの報告、セグメントの終了 次のコード例は、 storeItem
メソッドで始まるセグメントを示しており、Lambdaステートメントがスレッドプールで待機している時間を測定します。セグメントのタイミングを停止するには、 .end()
または.ignore()
のいずれかを呼び出す必要があります。親トランザクションの一部としてセグメントを報告したくない 場合は、 .ignore()
を呼び出します。それ以外の場合、親トランザクションの一部としてセグメントを報告するには、 .end()
を呼び出します。
private void storeItem(long id) {
Segment segment = NewRelic.getAgent().getTransaction().startSegment("storeItem");
segment.reportAsExternal(DatastoreParameters
.instance("localhost", 8080)
本サンプルでのエージェントAPIコールは
startSegment(...)
:コードの時間を計測するセグメントを開始します。このメソッドの詳細については、 Javadoc を参照してください。reportAsExternal(DatastoreParameters())
:時刻をデータストアの外部呼び出しに関連付けます。これは、APMに データストアデータ とともに表示されます。詳細については、 reportAsExternalAPI を参照してください。segment.end()
:このセグメントのタイミングを停止します。このメソッドの詳細については、 Javadoc を参照してください。3.New RelicのUIで非同期トレースを表示する メソッドが完了すると、APM は 1 つの外部呼び出しを含むトランザクション追跡を表示します。
ヒント デフォルトでは、エージェントは特定のトランザクション中に最大1000セグメントを追跡できます。この制限は、 segment_timeout
構成オプションを使用して変更できます。この制限を超えるトランザクションのトレースには、 segment_clamp
属性が含まれます。この制限を増やすと、エージェントのメモリ使用量が増える可能性があります。