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

SpringBoot基于Java Agent實(shí)現(xiàn)項(xiàng)目監(jiān)控

 更新時(shí)間:2025年07月04日 09:32:07   作者:風(fēng)象南  
在生產(chǎn)環(huán)境中,監(jiān)控對(duì)于項(xiàng)目問(wèn)題的分析排查變得尤為重要,本文將介紹如何利用Java Agent技術(shù)實(shí)現(xiàn)對(duì)SpringBoot應(yīng)用的無(wú)侵入式監(jiān)控,有需要的小伙伴可以了解下

在生產(chǎn)環(huán)境中,監(jiān)控對(duì)于項(xiàng)目問(wèn)題的分析排查變得尤為重要。

本文將介紹如何利用Java Agent技術(shù)實(shí)現(xiàn)對(duì)SpringBoot應(yīng)用的無(wú)侵入式監(jiān)控,幫助開(kāi)發(fā)人員在不修改源碼的情況下獲取應(yīng)用運(yùn)行時(shí)的關(guān)鍵指標(biāo)。

Java Agent簡(jiǎn)介

Java Agent是JDK 1.5引入的特性,它允許我們?cè)贘VM啟動(dòng)時(shí)或運(yùn)行時(shí)動(dòng)態(tài)地修改已加載的類字節(jié)碼,從而實(shí)現(xiàn)對(duì)應(yīng)用行為的增強(qiáng)或監(jiān)控。

Java Agent的核心優(yōu)勢(shì)在于能夠在不修改源代碼的情況下,對(duì)應(yīng)用進(jìn)行功能擴(kuò)展。

Java Agent主要有兩種使用方式:

啟動(dòng)時(shí)加載(premain) 運(yùn)行時(shí)加載(agentmain)

本文將主要關(guān)注啟動(dòng)時(shí)加載的方式。

技術(shù)原理

Java Agent的工作原理基于字節(jié)碼增強(qiáng)技術(shù),通過(guò)在類加載過(guò)程中修改字節(jié)碼來(lái)實(shí)現(xiàn)功能增強(qiáng)。

在SpringBoot應(yīng)用監(jiān)控場(chǎng)景中,我們可以利用Java Agent攔截關(guān)鍵方法的調(diào)用,收集執(zhí)行時(shí)間、資源使用情況等指標(biāo)。

主要技術(shù)棧:

  • Java Agent:提供字節(jié)碼修改的入口
  • Byte Buddy/ASM/Javassist:字節(jié)碼操作庫(kù)
  • SpringBoot:目標(biāo)應(yīng)用框架
  • Micrometer:指標(biāo)收集與暴露

實(shí)現(xiàn)步驟

1. 創(chuàng)建Agent項(xiàng)目

首先,我們需要?jiǎng)?chuàng)建一個(gè)獨(dú)立的Maven項(xiàng)目用于開(kāi)發(fā)Java Agent:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>demo</groupId>
        <artifactId>springboot-agent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>agent</artifactId>

    <dependencies>
        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy</artifactId>
            <version>1.14.5</version>
        </dependency>
        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy-agent</artifactId>
            <version>1.14.5</version>
        </dependency>

        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
            <version>1.10.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>21</source>
                    <target>21</target>
                    <encoding>utf-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.2.0</version>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Premain-Class>com.example.agent.MonitorAgent</Premain-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

2. 實(shí)現(xiàn)Agent主類

創(chuàng)建MonitorAgent類,實(shí)現(xiàn)premain方法:

package com.example.agent;

import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;

import java.lang.instrument.Instrumentation;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class MonitorAgent {

    private static final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();

    public static void premain(String arguments, Instrumentation instrumentation) {
        System.out.println("SpringBoot監(jiān)控Agent已啟動(dòng)...");
        log();
        // 使用ByteBuddy攔截SpringBoot的Controller方法
        new AgentBuilder.Default()
            .type(ElementMatchers.nameEndsWith("Controller"))
            .transform((builder, typeDescription, classLoader, module, protectionDomain) ->
                builder.method(ElementMatchers.isAnnotatedWith(
                        ElementMatchers.named("org.springframework.web.bind.annotation.RequestMapping")
                        .or(ElementMatchers.named("org.springframework.web.bind.annotation.GetMapping"))
                        .or(ElementMatchers.named("org.springframework.web.bind.annotation.PostMapping"))
                        .or(ElementMatchers.named("org.springframework.web.bind.annotation.PutMapping"))
                        .or(ElementMatchers.named("org.springframework.web.bind.annotation.DeleteMapping"))
                    ))
                    .intercept(MethodDelegation.to(ControllerInterceptor.class))
            )
            .installOn(instrumentation);
    }

    private static void log(){
        executorService.scheduleAtFixedRate(() -> {
            // 收集并打印性能指標(biāo)
            String text = MetricsCollector.scrape();
            System.out.println("===============");
            System.out.println(text);
        }, 0, 5, TimeUnit.SECONDS);
    }
}

3. 實(shí)現(xiàn)攔截器

創(chuàng)建Controller攔截器:

package com.example.agent;

import net.bytebuddy.implementation.bind.annotation.*;

import java.lang.reflect.Method;
import java.util.concurrent.Callable;

public class ControllerInterceptor {
    
    @RuntimeType
    public static Object intercept(
            @Origin Method method,
            @SuperCall Callable<?> callable,
            @AllArguments Object[] args) throws Exception {
        
        long startTime = System.currentTimeMillis();
        String className = method.getDeclaringClass().getName();
        String methodName = method.getName();
        
        try {
            // 調(diào)用原方法
            return callable.call();
        } catch (Exception e) {
            // 記錄異常信息
            MetricsCollector.recordException(className, methodName, e);
            throw e;
        } finally {
            long executionTime = System.currentTimeMillis() - startTime;
            // 收集性能指標(biāo)
            MetricsCollector.recordExecutionTime(className, methodName, executionTime);
        }
    }
}

4. 實(shí)現(xiàn)指標(biāo)收集

創(chuàng)建MetricsCollector類用于收集和暴露監(jiān)控指標(biāo):

package com.example.agent;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

public class MetricsCollector {
    
    private static final Map<String, AtomicLong> executionTimeMap = new ConcurrentHashMap<>();
    private static final Map<String, AtomicLong> invocationCountMap = new ConcurrentHashMap<>();
    private static final Map<String, AtomicLong> exceptionCountMap = new ConcurrentHashMap<>();
    
    public static void recordExecutionTime(String className, String methodName, long executionTime) {
        String key = className + "." + methodName;
        executionTimeMap.computeIfAbsent(key, k -> new AtomicLong(0)).addAndGet(executionTime);
        invocationCountMap.computeIfAbsent(key, k -> new AtomicLong(0)).incrementAndGet();
        
        // 輸出日志,實(shí)際項(xiàng)目中可能會(huì)發(fā)送到監(jiān)控系統(tǒng)
        System.out.printf("Controller執(zhí)行: %s, 耗時(shí): %d ms%n", key, executionTime);
    }
    
    public static void recordException(String className, String methodName, Exception e) {
        String key = className + "." + methodName;
        exceptionCountMap.computeIfAbsent(key, k -> new AtomicLong(0)).incrementAndGet();
        
        System.out.printf("Controller異常: %s, 異常類型: %s, 消息: %s%n", 
                key, e.getClass().getName(), e.getMessage());
    }
    
    public static void recordSqlExecutionTime(String className, String methodName, long executionTime) {
        String key = className + "." + methodName;
        executionTimeMap.computeIfAbsent(key, k -> new AtomicLong(0)).addAndGet(executionTime);
        invocationCountMap.computeIfAbsent(key, k -> new AtomicLong(0)).incrementAndGet();
        
        System.out.printf("SQL執(zhí)行: %s, 耗時(shí): %d ms%n", key, executionTime);
    }
    
    public static void recordSqlException(String className, String methodName, Exception e) {
        String key = className + "." + methodName;
        exceptionCountMap.computeIfAbsent(key, k -> new AtomicLong(0)).incrementAndGet();
        
        System.out.printf("SQL異常: %s, 異常類型: %s, 消息: %s%n", 
                key, e.getClass().getName(), e.getMessage());
    }
    
    // 獲取各種指標(biāo)的方法,可以被監(jiān)控系統(tǒng)調(diào)用
    public static Map<String, AtomicLong> getExecutionTimeMap() {
        return executionTimeMap;
    }
    
    public static Map<String, AtomicLong> getInvocationCountMap() {
        return invocationCountMap;
    }
    
    public static Map<String, AtomicLong> getExceptionCountMap() {
        return exceptionCountMap;
    }
}

5. 集成Prometheus與Grafana(可選)

為了更好地可視化監(jiān)控?cái)?shù)據(jù),我們可以將收集到的指標(biāo)暴露給Prometheus,并使用Grafana進(jìn)行展示。首先,添加Micrometer相關(guān)依賴:

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
    <version>1.10.0</version>
</dependency>

然后,修改MetricsCollector類,將收集到的指標(biāo)注冊(cè)到Micrometer:

package com.example.agent;

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class MetricsCollector {
    
    private static final PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
    private static final Map<String, Timer> timers = new ConcurrentHashMap<>();
    private static final Map<String, Counter> exceptionCounters = new ConcurrentHashMap<>();
    
    public static void recordExecutionTime(String className, String methodName, long executionTime) {
        String key = className + "." + methodName;
        getOrCreateTimer(key, "controller").record(executionTime, TimeUnit.MILLISECONDS);
        System.out.printf("Controller執(zhí)行: %s, 耗時(shí): %d ms%n", key, executionTime);
    }
    
    public static void recordException(String className, String methodName, Exception e) {
        String key = className + "." + methodName;
        getOrCreateExceptionCounter(key, "controller", e.getClass().getSimpleName()).increment();
        System.out.printf("Controller異常: %s, 異常類型: %s, 消息: %s%n", 
                key, e.getClass().getName(), e.getMessage());
    }
    
    public static void recordSqlExecutionTime(String className, String methodName, long executionTime) {
        String key = className + "." + methodName;
        getOrCreateTimer(key, "sql").record(executionTime, TimeUnit.MILLISECONDS);
        System.out.printf("SQL執(zhí)行: %s, 耗時(shí): %d ms%n", key, executionTime);
    }
    
    public static void recordSqlException(String className, String methodName, Exception e) {
        String key = className + "." + methodName;
        getOrCreateExceptionCounter(key, "sql", e.getClass().getSimpleName()).increment();
        System.out.printf("SQL異常: %s, 異常類型: %s, 消息: %s%n", 
                key, e.getClass().getName(), e.getMessage());
    }
    
    private static Timer getOrCreateTimer(String name, String type) {
        return timers.computeIfAbsent(name, k -> 
            Timer.builder("app.execution.time")
                .tag("name", name)
                .tag("type", type)
                .register(registry)
        );
    }
    
    private static Counter getOrCreateExceptionCounter(String name, String type, String exceptionType) {
        String key = name + "." + exceptionType;
        return exceptionCounters.computeIfAbsent(key, k -> 
            Counter.builder("app.exception.count")
                .tag("name", name)
                .tag("type", type)
                .tag("exception", exceptionType)
                .register(registry)
        );
    }
    
    // 獲取Prometheus格式的指標(biāo)數(shù)據(jù)
    public static String scrape() {
        return registry.scrape();
    }
    
    // 獲取注冊(cè)表,可以被其他組件使用
    public static MeterRegistry getRegistry() {
        return registry;
    }
}

6. 啟動(dòng)Agent并應(yīng)用到SpringBoot應(yīng)用

編譯并打包Agent項(xiàng)目后,可以通過(guò)JVM參數(shù)將Agent添加到SpringBoot應(yīng)用中:

java -javaagent:/path/to/springboot-monitor-agent.jar -jar your-springboot-app.jar

進(jìn)階擴(kuò)展

除了基本的監(jiān)控功能外,我們還可以對(duì)Agent進(jìn)行以下擴(kuò)展:

1. JVM指標(biāo)監(jiān)控

監(jiān)控JVM的內(nèi)存使用、GC情況、線程數(shù)等指標(biāo):

private static void monitorJvmMetrics(MeterRegistry registry) {
    // 注冊(cè)JVM內(nèi)存指標(biāo)
    new JvmMemoryMetrics().bindTo(registry);
    // 注冊(cè)GC指標(biāo)
    new JvmGcMetrics().bindTo(registry);
    // 注冊(cè)線程指標(biāo)
    new JvmThreadMetrics().bindTo(registry);
}

2. HTTP客戶端監(jiān)控

監(jiān)控應(yīng)用發(fā)起的HTTP請(qǐng)求:

new AgentBuilder.Default()
    .type(ElementMatchers.nameContains("RestTemplate")
          .or(ElementMatchers.nameContains("HttpClient")))
    .transform((builder, typeDescription, classLoader, module, protectionDomain) ->
        builder.method(ElementMatchers.named("execute")
                       .or(ElementMatchers.named("doExecute"))
                       .or(ElementMatchers.named("exchange")))
            .intercept(MethodDelegation.to(HttpClientInterceptor.class))
    )
    .installOn(instrumentation);

3. 分布式追蹤集成

與Zipkin或Jaeger等分布式追蹤系統(tǒng)集成,實(shí)現(xiàn)全鏈路追蹤:

public static void recordTraceInfo(String className, String methodName, String traceId, String spanId) {
    // 記錄追蹤信息
    MDC.put("traceId", traceId);
    MDC.put("spanId", spanId);
    // 處理邏輯...
}

優(yōu)勢(shì)與注意事項(xiàng)

優(yōu)勢(shì)

  • 無(wú)侵入性:不需要修改應(yīng)用源代碼
  • 靈活性:可以動(dòng)態(tài)決定要監(jiān)控的類和方法
  • 通用性:適用于任何基于SpringBoot的應(yīng)用
  • 運(yùn)行時(shí)監(jiān)控:可以實(shí)時(shí)收集應(yīng)用運(yùn)行數(shù)據(jù)

注意事項(xiàng)

  • 性能影響:字節(jié)碼增強(qiáng)會(huì)帶來(lái)一定的性能開(kāi)銷,需要合理選擇監(jiān)控點(diǎn)
  • 兼容性:需要確保Agent與應(yīng)用的JDK版本兼容
  • 穩(wěn)定性:Agent本身的異常不應(yīng)影響應(yīng)用主流程
  • 安全性:收集的數(shù)據(jù)可能包含敏感信息,需要注意數(shù)據(jù)安全

總結(jié)

在實(shí)際使用中,我們可以根據(jù)具體需求,對(duì)Agent進(jìn)行定制化開(kāi)發(fā),實(shí)現(xiàn)更加精細(xì)化的監(jiān)控。

同時(shí),可以將Agent與現(xiàn)有的監(jiān)控系統(tǒng)集成,構(gòu)建完整的應(yīng)用性能監(jiān)控體系。

到此這篇關(guān)于SpringBoot基于Java Agent實(shí)現(xiàn)項(xiàng)目監(jiān)控的文章就介紹到這了,更多相關(guān)SpringBoot Java Agent項(xiàng)目監(jiān)控內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

甘泉县| 丹凤县| 乌海市| 通江县| 宜章县| 邮箱| 灵石县| 合水县| 昭觉县| 烟台市| 三门县| 金沙县| 无锡市| 博兴县| 江达县| 临桂县| 府谷县| 灵山县| 嵩明县| 崇文区| 禹州市| 大方县| 房产| 大丰市| 公安县| 和田市| 西藏| 庆元县| 敦化市| 马关县| 大姚县| 沐川县| 襄城县| 青田县| 吐鲁番市| 广灵县| 平定县| 深水埗区| 称多县| 汨罗市| 玉田县|