最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用Arthas MCP對Java應用進行線上診斷的實踐指南

 更新時間:2026年05月14日 09:20:00   作者:zhaojiew10  
本文介紹了使用Arthas4.1.9診斷Java應用常見問題(CPU飆高、內(nèi)存泄漏、死鎖、線程池滿、慢方法、異常被吞)的方法,并提出構(gòu)建一個全自動的Java應用診斷系統(tǒng),通過AIagent結(jié)合MCP協(xié)議調(diào)用Arthas診斷工具,生成結(jié)構(gòu)化的診斷報告,簡化問題定位過程,需要的朋友可以參考下

前言

在實際的 Java 應用運維中,我們經(jīng)常遇到以下問題:

  • CPU 飆高:某個線程死循環(huán)導致服務器負載過高
  • 內(nèi)存泄漏:對象無法被回收,最終導致 OOM
  • 死鎖:多個線程互相等待,系統(tǒng)掛起
  • 線程池滿:任務堆積,新任務無法執(zhí)行
  • 慢方法:某些接口響應時間過長
  • 異常被吞:拋出異常但未記錄,難以排查

這些問題在本地開發(fā)時容易調(diào)試,但在生產(chǎn)環(huán)境中,我們通常只能通過日志和監(jiān)控來推測。Arthas 是阿里巴巴開源的 Java 診斷工具,可以幫助我們在線上環(huán)境快速定位問題。

傳統(tǒng)診斷方式下,我們需要登錄到目標環(huán)境,并通過Attach 到目標進程來進行線上診斷

curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
arithas> thread -b  # 檢測死鎖
arithas> dashboard  # 查看系統(tǒng)概況
arithas> trace com.example.MyClass myMethod  # 追蹤方法調(diào)用

本次使用的版本為Arthas 4.1.9,并通過java agent的方式隨應用自動啟動

# javaagent 方式
$ java -javaagent:arthas-agent.jar -jar app.jar

近期arthas新增了原生的MCP功能,讓我們能夠更加靈活地結(jié)合AI agent進行智能診斷。本文的核心目標為構(gòu)建一個全自動的 Java 應用診斷系統(tǒng)。讓AI Agent 能夠理解問題并選擇合適的診斷工具,生成結(jié)構(gòu)化的診斷報告,包含問題定位、根因分析、解決建議。

MCP 是一個開放協(xié)議,用于連接 AI 應用和外部工具(如數(shù)據(jù)庫、API、文件系統(tǒng)等)。它定義了標準化的接口,讓 AI Agent 可以通過統(tǒng)一的 API 調(diào)用各種工具。此外,本次的agent框架使用Strands Agent

# 直接獲得結(jié)構(gòu)化數(shù)據(jù)
tools = await session.list_tools()
result = await session.call_tool("thread", arguments={"arguments": ["-b"]})

整體的邏輯結(jié)構(gòu)如下

  • Python 負責進程啟停管理、等待 Arthas 服務就緒、創(chuàng)建智能代理、調(diào)度測試任務;
  • AI Agent 層基于本地通義千問編碼大模型,以診斷專家角色定位,依托 MCP 工具集調(diào)用能力,按理解任務 - 選用工具 - 分析數(shù)據(jù) - 輸出結(jié)論流程完成智能診斷;
  • MCP 客戶端層通過 HTTP/SSE 協(xié)議通信,配置令牌鑒權(quán),維持長連接會話,實現(xiàn)與服務端的標準協(xié)議交互;
  • Arthas MCP 服務層監(jiān)聽 9999 端口提供 MCP 接口,封裝線程、JVM、內(nèi)存、鏈路追蹤、反編譯等 Arthas 診斷工具能力;
  • 應用層預置 CPU 高占用、死鎖、內(nèi)存泄漏、異常、線程池耗盡、慢方法共 6 類故障測試場景,作為系統(tǒng)診斷的目標被測應用。

默認配置下,Arthas 會監(jiān)聽 127.0.0.1,導致外部無法連接。因此需要創(chuàng)建配置文件,明確指定監(jiān)聽地址和端口

# 配置文件路徑
~/.arthas/lib/4.1.9/arthas/arthas.properties
# MCP Endpoint 配置
arthas.mcpEndpoint=/mcp
# HTTP Server 配置
arthas.ip=0.0.0.0          # 綁定所有網(wǎng)卡,允許遠程訪問
arthas.httpPort=9999        # HTTP API 端口
arthas.telnetPort=0         # 禁用 Telnet(0 表示禁用)
# 認證配置
arthas.password=S2TotROqw5zmT8po7m7aiifdZxUzReSrknDslxe5tIBjMWlN5yrpatPIJogSAX0v

啟動應用

驗證 Arthas MCP Server

$ java -javaagent:~/.arthas/lib/4.1.9/arthas/arthas-agent.jar -Xmx256m -cp out <java-class> 
# 測試 MCP Server,上個命令啟動后會自動輸出Bearer認證token
$ curl -H "Authorization: Bearer S2TotROqw5zmT8po7m7aiifdZxUzReSrknDslxe5tIBjMWlN5yrpatPIJogSAX0v" \
        http://localhost:9999/mcp
# 返回 SSE 流
event: endpoint
data: {"method": "initialize", ...}

系統(tǒng)提示詞結(jié)構(gòu)

GENERIC_SYSTEM_PROMPT = """你是一個資深的 Java 應用診斷專家,擁有 10 年以上的生產(chǎn)環(huán)境排查經(jīng)驗。
## 當前診斷目標
{class_name} - {desc}
## 診斷原則
1. **觀察驅(qū)動**:首先觀察系統(tǒng)的整體狀態(tài)(JVM、線程、內(nèi)存),形成初步假設
2. **工具優(yōu)先級**:
   - 簡單查詢工具優(yōu)先(thread、jvm、memory、dashboard)
   - 如需流式工具(trace、monitor、watch),注意可能受認證限制
   - 使用 jad 反編譯源碼來理解代碼邏輯
3. **多角度驗證**:結(jié)合多個工具的結(jié)果交叉驗證,避免誤判
4. **根因?qū)?*:不只找到現(xiàn)象,要深入分析根本原因
5. **解決建議**:給出可操作的解決方案
## Arthas 工具速查
- `thread` / `thread -n 3` - 查看線程列表/CPU 最高線程
- `thread -b` - 檢測死鎖
- `thread <id>` - 查看特定線程的詳細堆棧
- `jvm` - JVM 運行時信息
- `memory` - 內(nèi)存使用情況
- `dashboard` / `dashboard -i 2000` - 系統(tǒng)概況/定期監(jiān)控
- `jad <ClassName>` - 反編譯類代碼
- `sysprop` - 系統(tǒng)屬性
- `logger` - 日志配置
## 認證說明
注意:部分流式工具(trace、monitor、watch)可能需要額外的會話認證。
如果遇到 401 錯誤,請改用其他工具組合(如 thread + jad)來達到同樣的診斷目標。
## 輸出要求
請按照以下格式輸出診斷結(jié)果:
1. **觀察發(fā)現(xiàn)**:通過工具看到的異?,F(xiàn)象
2. **問題定位**:問題的具體位置(類名、方法名、行號)
3. **根因分析**:為什么會出現(xiàn)這個問題
4. **解決方案**:具體的修復建議
開始診斷吧!
"""

strands agent構(gòu)造如下

# 3. 創(chuàng)建 MCP Client 和 Agent(使用通用系統(tǒng)提示詞)
arthas_mcp = MCPClient(
    lambda: streamablehttp_client(
        url="http://localhost:9999/mcp",
        headers={"Authorization": "Bearer S2TotROqw5zmTxxxxxpatPIJogSAX0v"}
    )
)
# 構(gòu)建通用系統(tǒng)提示詞
system_prompt = GENERIC_SYSTEM_PROMPT.format(
    class_name=class_name,
    desc=scenario['desc']
)
agent = Agent(
    model=model,
    system_prompt=system_prompt,
    tools=[arthas_mcp], # 將mcp注冊到agent中
)

CPU 高利用率

用戶報告:某個 Java 進程 CPU 使用率持續(xù)在 100% 左右,系統(tǒng)響應緩慢,影響其他服務正常運行。

  • CPU 使用率持續(xù)高位(100%)
  • 系統(tǒng)響應緩慢

模擬的故障代碼

public class CpuHighDemo {
    public static void main(String[] args) throws Exception {
        System.out.println("PID: " + ProcessHandle.current().pid());
        System.out.println("[CpuHighDemo] CPU飆高場景啟動...");
        // 創(chuàng)建一個死循環(huán)線程,執(zhí)行密集計算
        new Thread(() -> {
            while (true) {
                // 密集計算操作(冪運算)
                Math.pow(Math.random(), Math.random());
            }
        }, "cpu-busy-thread").start();  // ← 關(guān)鍵:線程名稱
        Thread.currentThread().join();
    }
}

啟動進程

啟動命令: java -javaagent:/home/ec2-user/.arthas/lib/4.1.9/arthas/arthas-agent.jar -Xmx256m -cp /home/ec2-user/workspace/java-diagnosis-demo/out CpuHighDemo
進程啟動 PID: 238433
等待 Arthas MCP Server 啟動...
? Arthas MCP Server 已就緒 (端口 9999)

測試日志

Tool #1: dashboard
Tool #2: thread
Tool #3: jad
Tool #4: thread

診斷結(jié)果

### 1. 觀察發(fā)現(xiàn)
通過使用 `dashboard` 和 `thread -n 5` 命令觀察到以下異?,F(xiàn)象:
- 線程 `cpu-busy-thread` (ID 27) 占用了高達 99.97% 的 CPU 使用率。
- 該線程處于 RUNNABLE 狀態(tài),表明它正在執(zhí)行任務且沒有等待或阻塞。
- 其他線程的 CPU 使用率都很低,幾乎可以忽略不計。
### 2. 問題定位
問題的具體位置在 `CpuHighDemo` 類中,具體是第 12 行的代碼:
```java
Math.pow(Math.random(), Math.random());
這個無限循環(huán)的操作導致了 CPU 使用率飆升。
### 3. 根因分析
從反編譯的代碼可以看出,在主線程啟動后創(chuàng)建了一個名為 `cpu-busy-thread` 的新線程。此線程執(zhí)行一個無限循環(huán),不斷地調(diào)用 `Math.pow(Math.random(), Math.random())` 方法。由于這是一個計算密集型操作,并且在無限循環(huán)中不斷執(zhí)行,因此會導致該線程持續(xù)占用大量的 CPU 資源,從而使得整個進程的 CPU 使用率達到接近 100% 的水平。
### 4. 解決方案
要解決這個問題,需要根據(jù)業(yè)務需求調(diào)整代碼邏輯:
1. **如果是測試場景**:可以在循環(huán)中添加適當?shù)男菝邥r間,例如使用 `Thread.sleep(1)` 來降低CPU利用率。
2. **如果是生產(chǎn)環(huán)境**:應該檢查是否有類似的死循環(huán)或者計算密集型任務未加控制地運行。
3. **長期優(yōu)化**:對于長時間運行的任務,考慮將其放入線程池中執(zhí)行,并設置合理的并發(fā)度以避免過度消耗系統(tǒng)資源。

死鎖

用戶報告程序突然掛起,不再處理任何請求,日志也沒有新的輸出。重啟后恢復,但幾分鐘后又掛起。

  • 程序掛起,無響應
  • 沒有錯誤日志

代碼實現(xiàn)

public class DeadlockDemo {
    private static final Object lockA = new Object();
    private static final Object lockB = new Object();
    public static void main(String[] args) throws Exception {
        System.out.println("PID: " + ProcessHandle.current().pid());
        System.out.println("[DeadlockDemo] 死鎖場景啟動...");
        // 線程 1:先獲取 lockA,再等待 lockB
        new Thread(() -> {
            synchronized (lockA) {              // ← 獲取 lockA
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                System.out.println("[Thread-1] 已獲取 lockA,等待 lockB...");
                synchronized (lockB) {          // ← 等待 lockB(被 Thread-2 持有)
                    System.out.println("[Thread-1] 已獲取 lockB");  // 永遠不會執(zhí)行
                }
            }
        }, "deadlock-thread-1").start();
        // 線程 2:先獲取 lockB,再等待 lockA
        new Thread(() -> {
            synchronized (lockB) {              // ← 獲取 lockB
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                System.out.println("[Thread-2] 已獲取 lockB,等待 lockA...");
                synchronized (lockA) {          // ← 等待 lockA(被 Thread-1 持有)
                    System.out.println("[Thread-2] 已獲取 lockA");  // 永遠不會執(zhí)行
                }
            }
        }, "deadlock-thread-2").start();
        Thread.currentThread().join();
    }
}

測試日志

Agent 開始自主診斷...
Tool #1: thread
Tool #2: thread
Tool #3: jad
Tool #4: jvm
Tool #5: memory
Tool #6: sysprop

完整診斷報告

### 1. 觀察發(fā)現(xiàn)
通過使用 `thread -b` 命令檢測到系統(tǒng)中存在死鎖:
- 線程 `deadlock-thread-2` (ID: 28) 被阻塞,等待獲取鎖 `java.lang.Object@26d694600`,該鎖被線程 `deadlock-thread-1` (ID: 27) 持有。
- 線程 `deadlock-thread-1` (ID: 27) 被阻塞,等待獲取鎖 `java.lang.Object@26d65717`,該鎖被線程 `deadlock-thread-2` (ID: 28) 持有。
這形成了一個典型的循環(huán)依賴死鎖。
進一步的 JVM 信息確認了這一點:
- `jvm` 命令輸出顯示 `DEADLOCK-COUNT` 為 2,證實了存在兩個線程參與了死鎖。
### 2. 問題定位
通過對 `DeadlockDemo` 類進行反編譯 (`jad` 命令),我們找到了問題的具體位置:
- **類名**: `DeadlockDemo`
- **涉及對象**:
  - `lockA` (在代碼第10行定義)
  - `lockB` (在代碼第11行定義)
- **線程1** (`deadlock-thread-1`):
  - 獲取了 `lockA` (第14行)
  - 嘗試獲取 `lockB` (第16行) 時被阻塞
- **線程2** (`deadlock-thread-2`):
  - 獲取了 `lockB` (第21行)
  - 嘗試獲取 `lockA` (第23行) 時被阻塞
### 3. 根因分析
出現(xiàn)死鎖的根本原因是兩個線程以不同的順序獲取相同的兩把鎖:
1. 線程1首先獲取 `lockA`,然后嘗試獲取 `lockB`。
2. 線程2首先獲取 `lockB`,然后嘗試獲取 `lockA`。
由于兩個線程都在等待對方釋放自己所需的鎖,而它們又都不釋放自己已經(jīng)持有的鎖,因此造成了永久性的阻塞,即死鎖。
這種經(jīng)典的"交叉鎖"問題是并發(fā)編程中最常見的死鎖形式之一。
### 4. 解決方案
為了避免此類死鎖的發(fā)生,可以采取以下幾種策略:
#### 方案一:統(tǒng)一加鎖順序
確保所有線程都以相同的順序獲取鎖。例如,在訪問這兩個資源時,總是先獲取 `lockA` 再獲取 `lockB`。
#### 方案二:使用超時機制
使用 `java.util.concurrent.locks.ReentrantLock` 的 `tryLock(long timeout, TimeUnit unit)` 方法。
#### 方案三:使用高級并發(fā)工具
考慮使用 `java.util.concurrent` 包中的高級同步工具,如 `Semaphore`、`CountDownLatch` 或者 `BlockingQueue`。

慢方法

用戶報告:某個業(yè)務接口響應時間過長(>1秒),影響用戶體驗。監(jiān)控顯示該接口平均響應時間持續(xù)在 1000ms 以上。

代碼實現(xiàn)

public class SlowMethodDemo {
    public static void main(String[] args) throws Exception {
        System.out.println("PID: " + ProcessHandle.current().pid());
        System.out.println("[SlowMethodDemo] 慢方法場景啟動...");

        new Thread(() -> {
            while (true) {
                doSlowBusiness();  // ← 慢方法
                try { Thread.sleep(1000); } catch (InterruptedException e) { break; }
            }
        }, "slow-method-thread").start();

        Thread.currentThread().join();
    }

    // 慢方法:總耗時約 1 秒
    static void doSlowBusiness() {
        step1();  // 200ms
        step2();  // 500ms
        step3();  // 300ms
    }

    static void step1() {
        try { Thread.sleep(200); } catch (InterruptedException e) {}
    }

    static void step2() {
        try { Thread.sleep(500); } catch (InterruptedException e) {}
    }

    static void step3() {
        try { Thread.sleep(300); } catch (InterruptedException e) {}
    }
}

測試日志

Tool #1: thread

Tool #2: jvm

Tool #3: memory

Tool #4: dashboard

Tool #5: sc

Tool #6: sm

Tool #7: jad

Tool #8: thread

Tool #9: thread

完整診斷報告

### 1. 觀察發(fā)現(xiàn)
通過一系列工具的綜合分析,我們發(fā)現(xiàn)了以下關(guān)鍵信息:

- **線程狀態(tài)**: `slow-method-thread` 線程處于 `TIMED_WAITING` 狀態(tài),表明它正在執(zhí)行一個定時等待操作。
- **CPU 使用**: 整體系統(tǒng)負載不高,沒有明顯的CPU瓶頸。
- **內(nèi)存使用**: JVM 內(nèi)存使用正常,GC活動較少,沒有出現(xiàn)頻繁Full GC的情況。
- **代碼結(jié)構(gòu)**: 通過反編譯 `SlowMethodDemo` 類,我們看到了其主要邏輯結(jié)構(gòu),特別是 `doSlowBusiness()` 方法及其調(diào)用的三個步驟方法。

### 2. 問題定位
具體的問題出現(xiàn)在 `SlowMethodDemo` 類的 `step2()` 方法中。
- **類名**: `SlowMethodDemo`
- **方法名**: `step2`
- **行號**: 第27行 (`Thread.sleep(500L);`)

### 3. 根因分析
通過對 `SlowMethodDemo` 的源碼分析,我們發(fā)現(xiàn)該類模擬了一個慢速業(yè)務處理流程:
```java
static void doSlowBusiness() {
    SlowMethodDemo.step1(); // 耗時200ms
    SlowMethodDemo.step2(); // 耗時500ms
    SlowMethodDemo.step3(); // 耗時300ms
}

其中,`step2()` 方法包含了長達500毫秒的睡眠操作:

```java
static void step2() {
    try {
        Thread.sleep(500L);
    } catch (InterruptedException e) {
        // empty catch block
    }
}
```
這個人為添加的延遲是導致整個業(yè)務接口響應時間超過1秒的主要原因。雖然這不是真實業(yè)務場景中的性能問題,但確實反映了在開發(fā)或測試環(huán)境中如何模擬慢速服務的一種常見做法。

### 4. 解決方案

針對這種情況,可以采取以下幾種措施:
1. **如果是測試環(huán)境**: 可以根據(jù)需要調(diào)整延遲時間或者移除不必要的延遲??梢允褂门渲庙梺砜刂七@些延遲。
2. **如果是生產(chǎn)環(huán)境問題**: 需要進一步分析 `step2()` 中實際執(zhí)行的業(yè)務邏輯,找出真正的性能瓶頸點。
3. **通用優(yōu)化建議**:
   - 將耗時操作分解為多個小任務并行處理
   - 引入緩存機制減少重復計算
   - 使用連接池管理數(shù)據(jù)庫和網(wǎng)絡連接
   - 定期進行性能壓測和代碼審查

以上就是使用Arthas MCP對Java應用進行線上診斷的實踐指南的詳細內(nèi)容,更多關(guān)于Arthas MCP對Java線上診斷的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C語言實現(xiàn)矩陣運算案例詳解

    C語言實現(xiàn)矩陣運算案例詳解

    這篇文章主要介紹了C語言實現(xiàn)矩陣運算案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • JAVA中Spring Boot的AOP切面編程是什么,如何使用?(實例代碼)

    JAVA中Spring Boot的AOP切面編程是什么,如何使用?(實例代碼)

    本文詳細介紹了SpringBoot中面向切面編程(AOP)的基本概念、核心術(shù)語、配置方法以及應用場景,涵蓋了AOP的五種通知類型、切點表達式、動態(tài)代理、切面優(yōu)先級與執(zhí)行順序等核心知識點,并通過實戰(zhàn)案例展示了AOP在微服務架構(gòu)中的應用潛力
    2026-01-01
  • Java實現(xiàn)購物管理系統(tǒng)

    Java實現(xiàn)購物管理系統(tǒng)

    這篇文章主要為大家詳細介紹了Java實現(xiàn)購物管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • SpringBoot集成canal實現(xiàn)示例解析

    SpringBoot集成canal實現(xiàn)示例解析

    這篇文章主要為大家介紹了springboot整合canal的示例實現(xiàn)解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多多進步,早日升職加薪
    2022-02-02
  • SpringBoot讀取自定義格式的Nacos配置的示例詳解

    SpringBoot讀取自定義格式的Nacos配置的示例詳解

    在開發(fā)的過程中,如果遇到一些特殊情況,例如Java項目讀取.net項目的配置問題,由于.net項目的配置文件是非標準格式的自定義文件,需要springboot項目讀取自定義格式的nacos配置文件,所以本文給大家介紹了SpringBoot讀取自定義格式的Nacos配置的方法
    2025-10-10
  • Java連接MYSQL數(shù)據(jù)庫的詳細步驟

    Java連接MYSQL數(shù)據(jù)庫的詳細步驟

    這篇文章主要為大家介紹了Java連接MYSQL數(shù)據(jù)庫的詳細步驟,感興趣的小伙伴們可以參考一下
    2016-05-05
  • 一文詳解Java如何防止DDoS攻擊

    一文詳解Java如何防止DDoS攻擊

    DDoS(分布式拒絕服務)攻擊是一種常見的網(wǎng)絡攻擊手段在?Java?應用開發(fā)中,了解?DDoS?攻擊的原理和防御策略至關(guān)重要,下面小編來和大家詳細講講
    2025-05-05
  • Java中的棧概述及JVM 中的棧結(jié)構(gòu)

    Java中的棧概述及JVM 中的棧結(jié)構(gòu)

    棧是一種受限的線性數(shù)據(jù)結(jié)構(gòu),只能在一端(稱為 棧頂(Top))進行插入和刪除操作,本文給大家介紹Java中的棧概述深入理解JVM中的棧結(jié)構(gòu),感興趣的朋友跟隨小編一起看看吧
    2025-10-10
  • Java中將List拆分為多個小list集合的實現(xiàn)代碼

    Java中將List拆分為多個小list集合的實現(xiàn)代碼

    這篇文章主要介紹了Java中如何將List拆分為多個小list集合,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • Springboot項目中定時任務的四種實現(xiàn)方式詳解

    Springboot項目中定時任務的四種實現(xiàn)方式詳解

    Spring的@Scheduled注解是一種非常簡單和便捷的實現(xiàn)定時任務的方式,通過在方法上添加@Scheduled注解,我們可以指定方法在特定的時間間隔或固定的時間點執(zhí)行,本文給大家介紹Springboot項目中定時任務的四種實現(xiàn)方式,感興趣的的朋友一起看看b
    2024-02-02

最新評論

闵行区| 阿城市| 浠水县| 米脂县| 诸城市| 襄汾县| 无锡市| 义马市| 云南省| 安岳县| 怀化市| 达孜县| 南靖县| 鹿邑县| 扎鲁特旗| 高青县| 肥东县| 冀州市| 南投县| 昂仁县| 新干县| 庆阳市| 东海县| 叶城县| 綦江县| 全南县| 宜城市| 达拉特旗| 巫山县| 四会市| 化德县| 巩留县| 安阳市| 新丰县| 来宾市| 德安县| 洛南县| 偃师市| 唐河县| 贞丰县| 南和县|