亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

基于SpringBoot實(shí)現(xiàn)一個(gè)方法級(jí)耗時(shí)監(jiān)控器

 更新時(shí)間:2025年09月12日 10:05:29   作者:風(fēng)象南  
本文介紹基于SpringBoot實(shí)現(xiàn)的輕量級(jí)耗時(shí)監(jiān)控器,通過(guò)AOP攔截方法并分級(jí)存儲(chǔ)數(shù)據(jù),提供可視化統(tǒng)計(jì)界面,支持調(diào)用次數(shù)、耗時(shí)、失敗次數(shù)分析及多維排序,適合中小型項(xiàng)目快速集成,無(wú)需復(fù)雜配置,需要的朋友可以參考下

一、需求痛點(diǎn)

線上應(yīng)用常見(jiàn)問(wèn)題:

某些接口偶爾變慢,但日志看不出問(wèn)題;

方法調(diào)用次數(shù)不透明,性能瓶頸難找;

線上出現(xiàn)失敗/超時(shí),但缺乏統(tǒng)計(jì)維度;

想要監(jiān)控,卻不想引入重量級(jí)的 APM 方案。

常見(jiàn) APM 工具功能強(qiáng)大,但部署復(fù)雜、學(xué)習(xí)成本高,不適合中小團(tuán)隊(duì)或者單機(jī)項(xiàng)目。

那有沒(méi)有可能,基于 SpringBoot 實(shí)現(xiàn)一個(gè)輕量級(jí)耗時(shí)監(jiān)控器,做到方法級(jí)監(jiān)控 + 可視化統(tǒng)計(jì) ?

二、功能目標(biāo)

我們希望監(jiān)控器能做到:

基礎(chǔ)監(jiān)控能力:

  • 方法調(diào)用次數(shù):統(tǒng)計(jì)某方法被調(diào)用了多少次
  • 耗時(shí)指標(biāo):平均耗時(shí)、最大耗時(shí)、最小耗時(shí)
  • 成功/失敗次數(shù):區(qū)分正常與異常調(diào)用
  • 多維排序:支持按調(diào)用次數(shù)、平均耗時(shí)、失敗次數(shù)等維度排序

進(jìn)階功能:

  • 時(shí)間段過(guò)濾:選擇時(shí)間范圍(如最近 5 分鐘、1 小時(shí)、1 天)查看數(shù)據(jù)
  • 接口搜索:快速定位特定接口的性能數(shù)據(jù)
  • 可視化控制臺(tái):實(shí)時(shí)展示接口調(diào)用統(tǒng)計(jì)

三、技術(shù)設(shè)計(jì)

整體思路

方法切面采集 使用 SpringAOP(基于攔截器亦可) 攔截 Controller 方法,在方法執(zhí)行前后記錄時(shí)間差、執(zhí)行結(jié)果(成功/失?。?。

分級(jí)數(shù)據(jù)存儲(chǔ) 采用分級(jí)時(shí)間桶策略:

  • 最近5分鐘:秒級(jí)精度統(tǒng)計(jì)
  • 最近1小時(shí):分鐘級(jí)聚合
  • 最近24小時(shí):小時(shí)級(jí)聚合
  • 最近7天:天級(jí)聚合

智能查詢 根據(jù)查詢時(shí)間范圍,自動(dòng)選擇最適合的數(shù)據(jù)粒度進(jìn)行聚合計(jì)算。

接口展示 提供 REST API 輸出統(tǒng)計(jì)數(shù)據(jù),前端使用 TailwindCSS + Alpine.js 渲染界面。

四、核心實(shí)現(xiàn)

1. 時(shí)間桶數(shù)據(jù)模型

@Data
public class TimeBucket {
    private final AtomicLong totalCount = new AtomicLong(0);
    private final AtomicLong successCount = new AtomicLong(0);
    private final AtomicLong failCount = new AtomicLong(0);
    private final LongAdder totalTime = new LongAdder();
    private volatile long maxTime = 0;
    private volatile long minTime = Long.MAX_VALUE;
    private final long bucketStartTime;
    private volatile long lastUpdateTime;

    public TimeBucket(long bucketStartTime) {
        this.bucketStartTime = bucketStartTime;
        this.lastUpdateTime = System.currentTimeMillis();
    }

    public synchronized void record(long duration, boolean success) {
        totalCount.incrementAndGet();
        if (success) {
            successCount.incrementAndGet();
        } else {
            failCount.incrementAndGet();
        }
        
        totalTime.add(duration);
        maxTime = Math.max(maxTime, duration);
        minTime = Math.min(minTime, duration);
        lastUpdateTime = System.currentTimeMillis();
    }

    public double getAvgTime() {
        long total = totalCount.get();
        return total == 0 ? 0.0 : (double) totalTime.sum() / total;
    }

    public double getSuccessRate() {
        long total = totalCount.get();
        return total == 0 ? 0.0 : (double) successCount.get() / total * 100;
    }
}

2. 分級(jí)采樣指標(biāo)模型

@Data
public class HierarchicalMethodMetrics {
    
    // 基礎(chǔ)統(tǒng)計(jì)信息
    private final AtomicLong totalCount = new AtomicLong(0);
    private final AtomicLong successCount = new AtomicLong(0);
    private final AtomicLong failCount = new AtomicLong(0);
    private final LongAdder totalTime = new LongAdder();
    private volatile long maxTime = 0;
    private volatile long minTime = Long.MAX_VALUE;
    private final String methodName;

    // 分級(jí)時(shí)間桶
    private final ConcurrentHashMap<Long, TimeBucket> secondBuckets = new ConcurrentHashMap<>();  // 最近5分鐘,秒級(jí)
    private final ConcurrentHashMap<Long, TimeBucket> minuteBuckets = new ConcurrentHashMap<>();  // 最近1小時(shí),分鐘級(jí)
    private final ConcurrentHashMap<Long, TimeBucket> hourBuckets = new ConcurrentHashMap<>();    // 最近24小時(shí),小時(shí)級(jí)
    private final ConcurrentHashMap<Long, TimeBucket> dayBuckets = new ConcurrentHashMap<>();     // 最近7天,天級(jí)

    public synchronized void record(long duration, boolean success) {
        long currentTime = System.currentTimeMillis();
        
        // 更新基礎(chǔ)統(tǒng)計(jì)
        totalCount.incrementAndGet();
        if (success) {
            successCount.incrementAndGet();
        } else {
            failCount.incrementAndGet();
        }
        totalTime.add(duration);
        maxTime = Math.max(maxTime, duration);
        minTime = Math.min(minTime, duration);

        // 分級(jí)記錄到不同時(shí)間桶
        recordToTimeBuckets(currentTime, duration, success);
        
        // 清理過(guò)期桶
        cleanupExpiredBuckets(currentTime);
    }

    public TimeRangeMetrics queryTimeRange(long startTime, long endTime) {
        List<TimeBucket.TimeBucketSnapshot> buckets = selectBucketsForTimeRange(startTime, endTime);
        return aggregateSnapshots(buckets, startTime, endTime);
    }
}

3. AOP 切面統(tǒng)計(jì)

@Slf4j
@Aspect
@Component
public class MethodMetricsAspect {
    
    private final ConcurrentHashMap<String, HierarchicalMethodMetrics> metricsMap = new ConcurrentHashMap<>();

    @Around("@within(org.springframework.web.bind.annotation.RestController) || " +
            "@within(org.springframework.stereotype.Controller)")
    public Object recordMetrics(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = buildMethodName(joinPoint);
        long startTime = System.nanoTime();
        boolean success = true;
        
        try {
            Object result = joinPoint.proceed();
            return result;
        } catch (Throwable throwable) {
            success = false;
            throw throwable;
        } finally {
            long duration = (System.nanoTime() - startTime) / 1_000_000; // Convert to milliseconds
            
            metricsMap.computeIfAbsent(methodName, HierarchicalMethodMetrics::new)
                     .record(duration, success);
        }
    }

    private String buildMethodName(ProceedingJoinPoint joinPoint) {
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        return className + "." + methodName + "()";
    }

    public Map<String, HierarchicalMethodMetrics> getMetricsSnapshot() {
        return new ConcurrentHashMap<>(metricsMap);
    }
}

4. 數(shù)據(jù)查詢接口

@RestController
@RequestMapping("/api/metrics")
@RequiredArgsConstructor
public class MetricsController {
    
    private final MethodMetricsAspect metricsAspect;

    @GetMapping
    public Map<String, Object> getMetrics(
            @RequestParam(required = false) Long startTime,
            @RequestParam(required = false) Long endTime,
            @RequestParam(required = false) String methodFilter) {
        
        Map<String, Object> result = new HashMap<>();
        Map<String, HierarchicalMethodMetrics> snapshot = metricsAspect.getMetricsSnapshot();
        
        // 應(yīng)用接口名過(guò)濾
        if (StringUtils.hasText(methodFilter)) {
            snapshot = snapshot.entrySet().stream()
                    .filter(entry -> entry.getKey().toLowerCase().contains(methodFilter.toLowerCase()))
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        }
        
        // 時(shí)間范圍查詢
        if (startTime != null && endTime != null) {
            snapshot.forEach((methodName, metrics) -> {
                HierarchicalMethodMetrics.TimeRangeMetrics timeRangeMetrics = 
                        metrics.queryTimeRange(startTime, endTime);
                Map<String, Object> metricData = buildTimeRangeMetricData(timeRangeMetrics);
                result.put(methodName, metricData);
            });
        } else {
            // 全量數(shù)據(jù)
            snapshot.forEach((methodName, metrics) -> {
                Map<String, Object> metricData = buildMetricData(metrics);
                result.put(methodName, metricData);
            });
        }
        
        return result;
    }

    @GetMapping("/recent/{minutes}")
    public Map<String, Object> getRecentMetrics(
            @PathVariable int minutes,
            @RequestParam(required = false) String methodFilter) {
        
        long endTime = System.currentTimeMillis();
        long startTime = endTime - (minutes * 60L * 1000L);
        
        return getMetrics(startTime, endTime, methodFilter);
    }

    @GetMapping("/summary")
    public Map<String, Object> getSummary(
            @RequestParam(required = false) Long startTime,
            @RequestParam(required = false) Long endTime,
            @RequestParam(required = false) String methodFilter) {
        
        // 匯總統(tǒng)計(jì)邏輯
        Map<String, HierarchicalMethodMetrics> snapshot = metricsAspect.getMetricsSnapshot();
        // ... 匯總計(jì)算
        return summary;
    }
}

5. 定時(shí)清理服務(wù)

@Service
@RequiredArgsConstructor
public class MetricsCleanupService {

    private final MethodMetricsAspect metricsAspect;

    @Value("${dashboard.metrics.max-age:3600000}")
    private long maxAge;

    @Scheduled(fixedRateString = "${dashboard.metrics.cleanup-interval:300000}")
    public void cleanupStaleMetrics() {
        try {
            metricsAspect.removeStaleMetrics(maxAge);
            int currentMethodCount = metricsAspect.getMetricsSnapshot().size();
            log.info("Metrics cleanup completed. Current methods being monitored: {}", currentMethodCount);
        } catch (Exception e) {
            log.error("Error during metrics cleanup", e);
        }
    }
}

五、前端可視化界面

核心功能實(shí)現(xiàn):

function metricsApp() {
    return {
        metrics: {},
        summary: {},
        timeRange: 'all',
        methodFilter: '',
        
        // 時(shí)間范圍設(shè)置
        setTimeRange(range) {
            this.timeRange = range;
            this.updateTimeRangeText();
            if (range !== 'custom') {
                this.fetchMetrics();
                this.fetchSummary();
            }
        },

        // 構(gòu)建API查詢URL
        buildApiUrl(endpoint) {
            let url = `/api/metrics${endpoint}`;
            const params = new URLSearchParams();
            
            // 添加時(shí)間參數(shù)
            if (this.timeRange !== 'all') {
                if (this.timeRange === 'custom') {
                    if (this.customStartTime && this.customEndTime) {
                        params.append('startTime', new Date(this.customStartTime).getTime());
                        params.append('endTime', new Date(this.customEndTime).getTime());
                    }
                } else {
                    const endTime = Date.now();
                    const startTime = endTime - (this.timeRange * 60 * 1000);
                    params.append('startTime', startTime);
                    params.append('endTime', endTime);
                }
            }
            
            // 添加搜索參數(shù)
            if (this.methodFilter.trim()) {
                params.append('methodFilter', this.methodFilter.trim());
            }
            
            return params.toString() ? url + '?' + params.toString() : url;
        },

        // 獲取監(jiān)控?cái)?shù)據(jù)
        async fetchMetrics() {
            this.loading = true;
            try {
                const response = await fetch(this.buildApiUrl(''));
                this.metrics = await response.json();
                this.lastUpdate = new Date().toLocaleTimeString();
            } catch (error) {
                console.error('Failed to fetch metrics:', error);
            } finally {
                this.loading = false;
            }
        }
    };
}

六、配置說(shuō)明

application.yml 配置

server:
  port: 8080

spring:
  application:
    name: springboot-api-dashboard
  aop:
    auto: true
    proxy-target-class: true

# 監(jiān)控配置
dashboard:
  metrics:
    cleanup-interval: 300000  # 清理間隔:5分鐘
    max-age: 3600000         # 最大存活時(shí)間:1小時(shí)
    debug-enabled: false     # 調(diào)試模式

logging:
  level:
    com.example.dashboard: INFO

Maven 依賴

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

七、使用示例

啟動(dòng)應(yīng)用

mvn clean install
mvn spring-boot:run

訪問(wèn)界面

http://localhost:8080/index.html

API 調(diào)用示例

# 獲取所有監(jiān)控?cái)?shù)據(jù)
curl http://localhost:8080/api/metrics

# 獲取最近5分鐘的數(shù)據(jù)
curl http://localhost:8080/api/metrics/recent/5

# 按時(shí)間范圍和接口名篩選
curl "http://localhost:8080/api/metrics?startTime=1640995200000&endTime=1641000000000&methodFilter=user"

# 獲取匯總統(tǒng)計(jì)
curl http://localhost:8080/api/metrics/summary

# 清空監(jiān)控?cái)?shù)據(jù)
curl -X DELETE http://localhost:8080/api/metrics

八、應(yīng)用場(chǎng)景

性能分析 快速找到最慢的方法,定位性能瓶頸;

穩(wěn)定性監(jiān)控 發(fā)現(xiàn)失敗次數(shù)多的接口,提前預(yù)警;

容量評(píng)估 統(tǒng)計(jì)高頻調(diào)用方法,輔助系統(tǒng)擴(kuò)容決策;

問(wèn)題排查 結(jié)合時(shí)間段篩選,精確定位問(wèn)題發(fā)生時(shí)間;

趨勢(shì)分析 通過(guò)不同時(shí)間粒度的數(shù)據(jù),分析接口性能趨勢(shì)。

九、優(yōu)勢(shì)特點(diǎn)

輕量級(jí)部署 無(wú)需外部依賴,單個(gè) JAR 包即可運(yùn)行;

即插即用 添加依賴后自動(dòng)啟用,無(wú)需復(fù)雜配置;

資源友好 采用分級(jí)采樣策略,內(nèi)存占用可控;

十、總結(jié)

通過(guò) Spring Boot AOP + 分級(jí)采樣 + 現(xiàn)代化前端,我們實(shí)現(xiàn)了一個(gè)功能完整的輕量級(jí) APM 監(jiān)控系統(tǒng):

  • 支持方法級(jí)監(jiān)控和時(shí)間段篩選
  • 提供直觀的可視化界面和搜索功能
  • 具備良好的性能表現(xiàn)和穩(wěn)定性
  • 開(kāi)箱即用,適合中小型項(xiàng)目快速集成

它不是 SkyWalking、Pinpoint 的替代品,但作為單機(jī)自研的小型 APM 解決方案,在簡(jiǎn)單性和實(shí)用性之間取得了很好的平衡。對(duì)于不需要復(fù)雜分布式追蹤,但希望有基礎(chǔ)監(jiān)控能力的項(xiàng)目來(lái)說(shuō),這是一個(gè)不錯(cuò)的選擇。

以上就是基于SpringBoot實(shí)現(xiàn)一個(gè)方法級(jí)耗時(shí)監(jiān)控器的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot耗時(shí)監(jiān)控器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 如何在?Spring?Boot?中使用?OpenAI?ChatGPT?API

    如何在?Spring?Boot?中使用?OpenAI?ChatGPT?API

    這篇文章主要介紹了如何在Spring?Boot中使用OpenAI?ChatGPT?API,我們探索了 OpenAI ChatGPT API 以生成對(duì)提示的響應(yīng),我們創(chuàng)建了一個(gè) Spring Boot 應(yīng)用程序,它調(diào)用 API 來(lái)生成對(duì)提示的響應(yīng),需要的朋友可以參考下
    2023-08-08
  • Struts2 的國(guó)際化實(shí)現(xiàn)方式示例

    Struts2 的國(guó)際化實(shí)現(xiàn)方式示例

    這篇文章主要介紹了Struts2 的國(guó)際化實(shí)現(xiàn)方式示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • 用IDEA創(chuàng)建SpringBoot項(xiàng)目的詳細(xì)步驟記錄

    用IDEA創(chuàng)建SpringBoot項(xiàng)目的詳細(xì)步驟記錄

    Idea有著非常簡(jiǎn)便的Spring Boot新建過(guò)程,同時(shí)依靠pom自動(dòng)下載依賴,下面這篇文章主要給大家介紹了關(guān)于用IDEA創(chuàng)建SpringBoot項(xiàng)目的詳細(xì)步驟,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • 基于Java開(kāi)發(fā)實(shí)現(xiàn)ATM系統(tǒng)

    基于Java開(kāi)發(fā)實(shí)現(xiàn)ATM系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了基于Java開(kāi)發(fā)實(shí)現(xiàn)ATM系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • 通過(guò)java字節(jié)碼分析學(xué)習(xí)對(duì)象初始化順序

    通過(guò)java字節(jié)碼分析學(xué)習(xí)對(duì)象初始化順序

    今天用了jmock對(duì)進(jìn)行單元測(cè)試編碼,發(fā)現(xiàn)一個(gè)比較奇怪的語(yǔ)法,static使用方法,見(jiàn)下面例子
    2013-11-11
  • Java設(shè)計(jì)模式之策略模式的使用(Strategy?Pattern)

    Java設(shè)計(jì)模式之策略模式的使用(Strategy?Pattern)

    策略模式是一種行為型設(shè)計(jì)模式,用于定義一系列算法并將每個(gè)算法封裝起來(lái),使它們可以互相替換,從而實(shí)現(xiàn)代碼的可維護(hù)性和靈活性,策略模式包含策略接口、具體策略類和上下文類,并通過(guò)將算法的選擇與使用分離,使得算法可以獨(dú)立變化
    2025-03-03
  • SpringMVC 通過(guò)commons-fileupload實(shí)現(xiàn)文件上傳功能

    SpringMVC 通過(guò)commons-fileupload實(shí)現(xiàn)文件上傳功能

    這篇文章主要介紹了SpringMVC 通過(guò)commons-fileupload實(shí)現(xiàn)文件上傳,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-02-02
  • Java Socket長(zhǎng)連接實(shí)現(xiàn)教程及標(biāo)準(zhǔn)示例

    Java Socket長(zhǎng)連接實(shí)現(xiàn)教程及標(biāo)準(zhǔn)示例

    Java Socket長(zhǎng)連接提供了一種持久通信方式,適用于需要低延遲和高效率的網(wǎng)絡(luò)應(yīng)用,本文將詳細(xì)介紹如何創(chuàng)建Java Socket長(zhǎng)連接的客戶端和服務(wù)端,包括它們的工作原理、實(shí)現(xiàn)細(xì)節(jié)、異常處理以及性能優(yōu)化,感興趣的朋友跟隨小編一起看看吧
    2025-09-09
  • Java使用sleep方法暫停線程Thread

    Java使用sleep方法暫停線程Thread

    這篇文章介紹了Java使用sleep方法暫停線程Thread,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-12-12
  • MySQL查詢字段實(shí)現(xiàn)字符串分割split功能的示例代碼

    MySQL查詢字段實(shí)現(xiàn)字符串分割split功能的示例代碼

    本文主要介紹了MySQL查詢字段實(shí)現(xiàn)字符串分割split功能的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01

最新評(píng)論