会社で障害問題を特定する際、一般的にはコード内の重要な部分にログを出力し、環境コンテナ内で Jar パッケージを交換する形式で問題の特定分析を行います。しかし、このような問題の特定プロセスは非常に面倒です:
- まず、ログ出力の網羅性を確保する必要があります。万が一、重要な情報が出力されていない場合は、再度ログを出力し、パッケージを交換し、サービスを再起動する必要があり、一連のプロセスで多くの時間を浪費します;
- 次に、すべての環境でパッケージの交換やサービスの再起動が実行できるわけではありません。
そのため、最近はより良い障害問題の特定方法を探しており、阿里が開発したオープンソースの Java サービス診断ツールがなかなか良さそうだと気付きました。このツールは、メソッド呼び出しの引数、戻り値、呼び出された呼び出しパス、呼び出し時間、メソッド呼び出し回数、成功回数、失敗回数などを記録できるため、学習して記録しておきます。
arthas とは#
公式紹介:
Arthas は、オンライン監視診断製品で、グローバルな視点からアプリケーションのロード、メモリ、GC、スレッドの状態情報をリアルタイムで確認でき、アプリケーションコードを変更することなく、ビジネス問題を診断できます。これには、メソッド呼び出しの引数、例外の確認、メソッド実行時間の監視、クラスロード情報などが含まれ、オンライン問題のトラブルシューティング効率を大幅に向上させます。
実行環境#
- JDK 6 以上の環境のみサポート
- Java で書かれており、クロスプラットフォームをサポート:Linux(主に)、Mac、Windows
特徴#
- コマンドラインインタラクティブモードを採用
Tab
キーによる自動補完機能を提供
初歩的な使用#
会社で使用している環境は主にコンテナ内にあるため、以下では Linux 環境でこのツールをどのように使用するかを主に記録します。
使用パッケージのダウンロード#
会社の環境は内部ネットワーク環境で、直接 Github にアクセスしてインストールパッケージをダウンロードすることができないため、マシンのネットワーク問題でダウンロードできないのを防ぐために、手動で Github からダウンロードし、サービスコンテナ内にコピーして使用します。
Github から完全なインストールパッケージをダウンロードします。ダウンロードリンク:https://github.com/alibaba/arthas/releases
コンテナ環境での解凍#
# arthas専用のディレクトリを作成します。解凍後に多くのファイルが生成されるためです。
mkdir arthas
# 作成したディレクトリarthasに解凍します。
unzip -d arthas arthas-bin.zip
アンインストール#
問題を特定した後は戦場をきれいにする必要があるため、このツールのアンインストール方法も記録しておきます。
以下の 3 つのステップを実行することでツールをアンインストールできます。
rm -rf arthas
rm -rf ~/.arthas/
rm -rf ~/logs
実行#
まず、停止しない Java プログラムサービスを起動します。公式のインストールパッケージには、練習用の Jar パッケージ
math-game.jar
が付属しています(ただし、一般的に私たちのサービスも常に実行されていますので、ここでは公式のパッケージを記録として使用します)。
# このJavaプログラムを起動します。独自のサービスがある場合はこのステップをスキップできます。
java -jar math-game.jar
次に、arthas を起動します。
# 1、起動
java -jar arthas-boot.jar
# 2、アタッチするJavaサービスを選択し、プロセス番号を入力してEnterを押します(ここには1つのプロセスしかありません。つまりプロセス1です)。
1
# arthasのロゴが表示されると、arthasがこのプロセス1サービスにアタッチされたことを示します。
よく使うコマンド#
help#
# helpを入力すると、Arthasに関連するコマンドのヘルプ情報を取得できます。
[arthas@421554]$ help
NAME DESCRIPTION
help Arthasヘルプを表示
auth 現在のセッションを認証
keymap 指定された接続のすべての利用可能なキー マップを表示。
sc JVMによってロードされたすべてのクラスを検索
sm JVMによってロードされたクラスのメソッドを検索
classloader クラスローダー情報を表示
jad クラスを逆コンパイル
getstatic クラスの静的フィールドを表示
monitor メソッド実行統計を監視します。例:合計/成功/失敗のカウント、平均RT、失敗率など。
stack 指定されたクラスとメソッドのスタックトレースを表示
thread スレッド情報、スレッドスタックを表示
trace 指定されたメソッド呼び出しの実行時間をトレースします。
watch 指定されたメソッド呼び出しの入力/出力パラメータ、戻りオブジェクト、およびスローされた例外を表示
tt タイムトンネル
jvm 対象JVM情報を表示
memory jvmメモリ情報を表示。
perfcounter perfカウンター情報を表示。
ognl ognl式を実行します。
mc メモリコンパイラ、Javaファイルをバイトコードとメモリ内のクラスファイルにコンパイルします。
redefine クラスを再定義します。@see Instrumentation#redefineClasses(ClassDefinition...)
retransform クラスを再変換します。@see Instrumentation#retransformClasses(Class...)
dashboard 対象jvmのスレッド、メモリ、gc、vm、tomcat情報の概要。
dump JVMからクラスバイト配列をダンプ
heapdump ヒープダンプ
options 様々なArthasオプションを表示および変更します。
cls 画面をクリア
reset 強化されたすべてのクラスをリセット
version Arthasのバージョンを表示
session 現在のセッション情報を表示
sysprop システムプロパティを表示および変更します。
sysenv システム環境を表示。
vmoption vm診断オプションを表示および更新します。
logger ロガー情報を印刷し、ロガーレベルを更新します。
history コマンド履歴を表示
cat ファイルを連結して印刷します。
base64 Base64表現を使用してエンコードおよびデコードします。
echo 引数を標準出力に書き込みます。
pwd 作業ディレクトリ名を返します。
mbean mbean情報を表示
grep パイプ用のgrepコマンド。
tee パイプ用のteeコマンド。
profiler Async Profiler。https://github.com/jvm-profiling-tools/async-profiler
vmtool jvmツール
stop Arthasサーバーを停止/シャットダウンし、コンソールを終了します。
jfr Java Flight Recorder Command
dashboard#
ダッシュボード:現在のシステムのリアルタイムデータパネルを表示します。普段このダッシュボードがないときは、一般的に Linux に付属の
top
コマンドを使用してシステムの実行情報を確認します。
dashboardを入力し、Enter
を押すと、現在のプロセスの情報が表示されます。ctrl+c
を押すか、q
を入力すると実行を中断できます。
表示される情報は大きく 3 つのブロックに分かれています:
- 一番上はスレッド関連の情報
- 中間の領域は JVM メモリ関連の情報
- 一番下は Java の実行環境情報
具体的な各列の情報については公式文書を参照してください。
thread#
現在のスレッド情報スタックを確認します。
パラメータがない場合、最初のページのスレッド情報を表示します。#
thread
デフォルトでは CPU 増分時間の降順で並べ替えられ、最初のページのデータのみが表示されます。
現在最も忙しい上位 N 個のスレッドを一度に表示し、スタックを印刷します。#
thread -n N
thread --all、すべての一致するスレッドを表示します。#
# すべての一致するスレッド情報を表示します。時には、分析のためにすべてのJVMのスレッドデータを取得する必要があります。
thread --all
thread id、指定されたスレッドの実行スタックを表示します。#
[arthas@421554]$ thread 1
"main" Id=1 TIMED_WAITING
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:342)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at demo.MathGame.main(MathGame.java:17)
thread -b、現在他のスレッドをブロックしているスレッドを見つけます。#
thread -b
watch#
指定されたメソッドの呼び出し状況を観察します。
観察できる内容:
メソッドの戻り値、引数、メソッドがスローした例外、OGNL 式を記述することで対応する変数の確認も可能です。
関数呼び出しの戻り時の引数、this オブジェクト、戻り値を観察します。#
# 観察する次元のデフォルト値は{params, target, returnObj}です。以下は関数呼び出しの戻り時の引数、thisオブジェクト、戻り値を観察しています。-xは出力結果属性の遍歴深度を示し、子オブジェクトの深度です。最大深度は4です。
[arthas@421554]$ watch demo.MathGame primeFactors -x 2
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 28 ms, listenerId: 2
method=demo.MathGame.primeFactors location=AtExit
ts=2023-05-10 00:25:42; [cost=0.106133ms] result=@ArrayList[
@Object[][
@Integer[1],
],
@MathGame[
random=@Random[java.util.Random@254989ff],
illegalArgumentCount=@Integer[85442],
],
@ArrayList[
@Integer[103],
@Integer[1667],
],
]
# 深度を3に変更します。
[arthas@421554]$ watch demo.MathGame primeFactors -x 3
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 26 ms, listenerId: 3
method=demo.MathGame.primeFactors location=AtExit
ts=2023-05-10 00:26:17; [cost=0.34344ms] result=@ArrayList[
@Object[][
@Integer[1],
],
@MathGame[
random=@Random[
serialVersionUID=@Long[3905348978240129619],
seed=@AtomicLong[97774455668942],
multiplier=@Long[25214903917],
addend=@Long[11],
mask=@Long[281474976710655],
DOUBLE_UNIT=@Double[1.1102230246251565E-16],
BadBound=@String[bound must be positive],
BadRange=@String[bound must be greater than origin],
BadSize=@String[size must be non-negative],
seedUniquifier=@AtomicLong[-3282039941672302964],
nextNextGaussian=@Double[0.0],
haveNextNextGaussian=@Boolean[false],
serialPersistentFields=@ObjectStreamField[][isEmpty=false;size=3],
unsafe=@Unsafe[sun.misc.Unsafe@5d099f62],
seedOffset=@Long[24],
],
illegalArgumentCount=@Integer[85459],
],
@ArrayList[
@Integer[7],
@Integer[21313],
],
]
関数呼び出し前と関数戻り後を同時に観察します。#
[arthas@421554]$ watch demo.MathGame primeFactors "{params,target,returnObj}" -x 2 -b -s
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 39 ms, listenerId: 6
method=demo.MathGame.primeFactors location=AtEnter
ts=2023-05-10 00:30:00; [cost=0.036373ms] result=@ArrayList[
@Object[][
@Integer[163405],
],
@MathGame[
random=@Random[java.util.Random@254989ff],
illegalArgumentCount=@Integer[85576],
],
null,
]
method=demo.MathGame.primeFactors location=AtExit
ts=2023-05-10 00:30:00; [cost=1.4721136326180643E10ms] result=@ArrayList[
@Object[][
@Integer[1],
],
@MathGame[
random=@Random[java.util.Random@254989ff],
illegalArgumentCount=@Integer[85576],
],
@ArrayList[
@Integer[5],
@Integer[11],
@Integer[2971],
],
]
現在のオブジェクト内の属性を観察します。#
関数の実行前後に現在のオブジェクト内の属性を確認したい場合は、target キーワードを使用します。target は現在のオブジェクトを示し、target.field_name を使用して現在のオブジェクトの特定の属性にアクセスします。
[arthas@421554]$ watch demo.MathGame primeFactors 'target.illegalArgumentCount'
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 31 ms, listenerId: 7
method=demo.MathGame.primeFactors location=AtExit
ts=2023-05-10 00:33:50; [cost=0.081212ms] result=@Integer[85676]
method=demo.MathGame.primeFactors location=AtExceptionExit
ts=2023-05-10 00:33:51; [cost=0.102672ms] result=@Integer[85677]
trace#
メソッド内部の呼び出しパスを表示し、パス上の各ノードでの時間を出力します。サービス間の呼び出し時間が長すぎる場合に使用します。
[arthas@421554]$ trace demo.MathGame run
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 55 ms, listenerId: 9
`---ts=2023-05-11 00:24:38;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@74a14482
`---[0.54719ms] demo.MathGame:run()
+---[20.76% 0.113574ms ] demo.MathGame:primeFactors() #24
`---[53.28% 0.29155ms ] demo.MathGame:print() #25
- 出力結果の #24 は、ソースファイルの 24 行目で
primeFactors()
関数が呼び出されたことを示します。 - 出力結果の #25 は、ソースファイルの 25 行目で
print()
関数が呼び出されたことを示します。
stack#
現在のメソッドが呼び出された呼び出しパスを出力します。このメソッド(多くの場所から呼び出されている)をどこから実行したかを知りたい場合にこのコマンドを使用します(根源を探るのに適しています)。
arthas@421554]$ stack demo.MathGame primeFactors
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 38 ms, listenerId: 12
ts=2023-05-11 00:32:43;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@74a14482
@demo.MathGame.primeFactors()
at demo.MathGame.run(MathGame.java:24)
at demo.MathGame.main(MathGame.java:16)
jad#
指定されたロード済みクラスのソースコードを逆コンパイルし、オンラインでビジネスロジックを理解しやすくします。逆コンパイルされたコードは構文ハイライト付きです。
jad demo.MathGame