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

基于SpringBoot從零構(gòu)建生產(chǎn)級OOM監(jiān)測預(yù)警系統(tǒng)

 更新時間:2026年03月30日 08:33:15   作者:J_liaty  
OOM是Java應(yīng)用在生產(chǎn)環(huán)境中最致命的殺手之一,一次未及時發(fā)現(xiàn)的內(nèi)存泄漏,足以讓整個服務(wù)集群在數(shù)分鐘內(nèi)崩潰,造成不可估量的業(yè)務(wù)損失,本文將從技術(shù)原理到工程實踐,帶你構(gòu)建一套完整的Spring Boot OOM監(jiān)測預(yù)警系統(tǒng),需要的朋友可以參考下

深入剖析OOM監(jiān)測的完整技術(shù)棧,手把手教你打造生產(chǎn)級內(nèi)存監(jiān)控方案

前言

OOM(Out Of Memory)是Java應(yīng)用在生產(chǎn)環(huán)境中最致命的殺手之一。一次未及時發(fā)現(xiàn)的內(nèi)存泄漏,足以讓整個服務(wù)集群在數(shù)分鐘內(nèi)崩潰,造成不可估量的業(yè)務(wù)損失。

本文將從技術(shù)原理到工程實踐,帶你構(gòu)建一套完整的Spring Boot OOM監(jiān)測預(yù)警系統(tǒng)。這套方案已在多個生產(chǎn)環(huán)境中驗證,具備以下核心優(yōu)勢:

  • 輕量級集成:純Spring Boot生態(tài),零侵入
  • 智能預(yù)警:支持多閾值、冷卻機制,避免誤報
  • 多渠道通知:日志 + 控制臺 + 郵件,可擴展釘釘/企業(yè)微信
  • 企業(yè)級友好:無縫對接Prometheus + Grafana

1. OOM本質(zhì)與監(jiān)測原理

1.1 什么是OOM?

OOM(Out Of Memory)即內(nèi)存溢出,當(dāng)JVM申請內(nèi)存時,可用內(nèi)存不足以滿足需求,就會拋出OutOfMemoryError。常見場景包括:

  • 堆內(nèi)存溢出:對象創(chuàng)建過多或內(nèi)存泄漏
  • Metaspace溢出:加載類過多或動態(tài)生成類
  • 直接內(nèi)存溢出:NIO使用直接內(nèi)存過多

1.2 監(jiān)測原理

JVM提供了豐富的管理接口,主要通過java.lang.management包中的MBean(Management Bean)獲取運行時數(shù)據(jù):

// 獲取內(nèi)存管理MBean
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
// 獲取堆內(nèi)存使用情況
MemoryUsage heapUsage = memoryMXBean.getHeapMemoryUsage();

MemoryUsage關(guān)鍵指標(biāo)說明

指標(biāo)含義說明
init初始內(nèi)存JVM啟動時分配的內(nèi)存大小
used已用內(nèi)存當(dāng)前實際使用的內(nèi)存大小
committed已提交內(nèi)存JVM已向操作系統(tǒng)申請的內(nèi)存
max最大內(nèi)存JVM可使用的最大內(nèi)存(由-Xmx決定)

核心計算公式

內(nèi)存使用率 = (已用內(nèi)存 / 最大內(nèi)存) × 100%

1.3 監(jiān)測策略

我們采用定時采樣 + 閾值判斷的策略:

  1. 定時采樣:每隔30秒(可配置)采集一次內(nèi)存數(shù)據(jù)
  2. 閾值判斷:當(dāng)使用率超過設(shè)定閾值(如85%)時觸發(fā)預(yù)警
  3. 冷卻機制:觸發(fā)預(yù)警后進(jìn)入冷卻期(如5分鐘),避免頻繁報警
  4. 自動恢復(fù):內(nèi)存使用率低于閾值時,自動退出冷卻狀態(tài)

2. 整體架構(gòu)設(shè)計

2.1 系統(tǒng)架構(gòu)圖

┌─────────────────────────────────────────────────────────────┐
│                     Spring Boot應(yīng)用                          │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌──────────────┐         ┌──────────────┐                  │
│  │   定時任務(wù)   │         │  REST接口    │                  │
│  │ @Scheduled   │         │  手動觸發(fā)    │                  │
│  └──────┬───────┘         └──────┬───────┘                  │
│         │                        │                          │
│         └────────┬───────────────┘                          │
│                  │                                          │
│         ┌────────▼────────┐                                 │
│         │ OomAlertService │  ?── 預(yù)警核心服務(wù)                │
│         └────────┬────────┘                                 │
│                  │                                          │
│         ┌────────▼────────┐                                 │
│         │MemoryMonitor    │  ?── 內(nèi)存數(shù)據(jù)采集                │
│         │Service         │                                 │
│         └────────┬────────┘                                 │
│                  │                                          │
│         ┌────────▼────────┐                                 │
│         │  JVM MBean      │  ?── 底層數(shù)據(jù)源                  │
│         │  (JMX)          │                                 │
│         └─────────────────┘                                 │
│                                                              │
│         預(yù)警通道                                              │
│    ┌─────┴─────┬─────┬─────┐                               │
│    ▼           ▼     ▼     ▼                               │
│  日志        控制臺  郵件  (可擴展)                         │
│                                                              │
└─────────────────────────────────────────────────────────────┘
                         │
                         ▼
              ┌──────────────────┐
              │   Prometheus     │  ?── 企業(yè)級監(jiān)控
              │   + Grafana      │
              └──────────────────┘

2.2 核心組件職責(zé)

組件類名職責(zé)
內(nèi)存采集MemoryMonitorService通過JVM MBean獲取內(nèi)存數(shù)據(jù),計算使用率
預(yù)警服務(wù)OomAlertService定時檢查內(nèi)存狀態(tài),觸發(fā)預(yù)警,管理冷卻期
配置管理MonitorConfig啟用定時任務(wù),管理全局配置
接口 暴露OomMonitorController提供REST接口用于手動觸發(fā)和查詢

3. 核心代碼實現(xiàn)

3.1 MemoryMonitorService - 內(nèi)存數(shù)據(jù)采集

核心職責(zé):封裝JVM MBean調(diào)用,提供簡潔的內(nèi)存數(shù)據(jù)接口。

package com.example.monitor.service;

import org.springframework.stereotype.Service;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;

/**
 * 內(nèi)存監(jiān)測核心服務(wù)
 * 
 * <p>基于JVM MBean(Management Bean)實現(xiàn)內(nèi)存數(shù)據(jù)采集,
 * 提供堆內(nèi)存、非堆內(nèi)存的使用情況查詢,以及使用率計算。
 * 
 * <p><b>設(shè)計要點:</b>
 * <ul>
 *   <li>單例設(shè)計:MemoryMXBean本身是JVM級單例,無需額外同步</li>
 *   <li>輕量級:采集操作耗時<1ms,對應(yīng)用性能無影響</li>
 *   <li>無狀態(tài):不保存歷史數(shù)據(jù),僅提供實時查詢</li>
 * </ul>
 * 
 * @author 技術(shù)團隊
 * @since 1.0
 */
@Service
public class MemoryMonitorService {

    /**
     * JVM內(nèi)存管理MBean
     * 
     * <p>通過ManagementFactory獲取,是JVM提供的標(biāo)準(zhǔn)管理接口。
     * 獲取堆內(nèi)存和非堆內(nèi)存的實時使用數(shù)據(jù)。
     * 
     * <p><b>注意:</b>
     * <ul>
     *   <li>堆內(nèi)存:存儲Java對象實例,是OOM的高發(fā)區(qū)域</li>
     *   <li>非堆內(nèi)存:包括Metaspace(類元數(shù)據(jù))、CodeCache(編譯代碼)等</li>
     * </ul>
     */
    private static final MemoryMXBean MEMORY_MX_BEAN = ManagementFactory.getMemoryMXBean();

    /**
     * 預(yù)定義的MB字節(jié)數(shù),用于避免重復(fù)計算
     */
    private static final long BYTES_PER_MB = 1024L * 1024L;

    /**
     * 獲取堆內(nèi)存使用情況
     * 
     * <p><b>堆內(nèi)存:</b>存儲Java對象實例,由GC進(jìn)行垃圾回收。
     * 通過-Xms和-Xmx參數(shù)控制初始大小和最大大小。
     * 
     * @return MemoryUsage對象,包含init、used、committed、max四個指標(biāo)
     */
    public MemoryUsage getHeapMemoryUsage() {
        return MEMORY_MX_BEAN.getHeapMemoryUsage();
    }

    /**
     * 獲取非堆內(nèi)存使用情況
     * 
     * <p><b>非堆內(nèi)存:</b>包括Metaspace(類元數(shù)據(jù))、CodeCache(JIT編譯后的代碼)等。
     * Metaspace溢出通常由加載類過多或動態(tài)生成類導(dǎo)致。
     * 
     * @return MemoryUsage對象,包含非堆內(nèi)存的使用數(shù)據(jù)
     */
    public MemoryUsage getNonHeapMemoryUsage() {
        return MEMORY_MX_BEAN.getNonHeapMemoryUsage();
    }

    /**
     * 計算堆內(nèi)存使用率(百分比)
     * 
     * <p><b>計算公式:</b>
     * <pre>使用率 = (已用內(nèi)存 / 最大內(nèi)存) × 100%</pre>
     * 
     * <p><b>邊界情況處理:</b>
     * <ul>
     *   <li>當(dāng)max ≤ 0時(JVM未設(shè)置-Xmx),返回0.0避免除零錯誤</li>
     *   <li>返回值保留雙精度浮點數(shù),便于精確比較</li>
     * </ul>
     * 
     * @return 堆內(nèi)存使用率,范圍[0, 100]
     */
    public double getHeapMemoryUsagePercentage() {
        MemoryUsage usage = getHeapMemoryUsage();
        long max = usage.getMax();
        
        // 邊界處理:JVM未設(shè)置-Xmx時max可能為-1或0
        if (max <= 0) {
            return 0.0;
        }
        
        long used = usage.getUsed();
        return (double) used / max * 100.0;
    }

    /**
     * 判斷是否觸發(fā)OOM預(yù)警
     * 
     * <p>比較當(dāng)前使用率與設(shè)定的閾值,當(dāng)使用率≥閾值時返回true。
     * 
     * @param threshold 閾值(百分比),如85.0表示85%
     * @return true表示存在OOM風(fēng)險,false表示正常
     * @throws IllegalArgumentException 如果threshold不在[0, 100]范圍內(nèi)
     */
    public boolean isOomRisk(double threshold) {
        // 參數(shù)校驗
        if (threshold < 0 || threshold > 100) {
            throw new IllegalArgumentException("閾值必須在[0, 100]范圍內(nèi),當(dāng)前值: " + threshold);
        }
        
        double usage = getHeapMemoryUsagePercentage();
        return usage >= threshold;
    }

    /**
     * 獲取詳細(xì)的內(nèi)存使用信息(用于日志輸出)
     * 
     * <p>返回格式化的字符串,包含:
     * <ul>
     *   <li>堆內(nèi)存:已用、已提交、最大、使用率</li>
     *   <li>非堆內(nèi)存:已用、已提交</li>
     * </ul>
     * 
     * <p><b>單位轉(zhuǎn)換:</b>所有字節(jié)數(shù)轉(zhuǎn)換為MB,便于閱讀。
     * 
     * @return 格式化的內(nèi)存信息字符串
     */
    public String getMemoryInfo() {
        MemoryUsage heap = getHeapMemoryUsage();
        MemoryUsage nonHeap = getNonHeapMemoryUsage();

        return String.format(
            "堆內(nèi)存 - 已用: %dMB, 已提交: %dMB, 最大: %dMB, 使用率: %.2f%% | " +
            "非堆內(nèi)存 - 已用: %dMB, 已提交: %dMB",
            heap.getUsed() / BYTES_PER_MB,
            heap.getCommitted() / BYTES_PER_MB,
            heap.getMax() / BYTES_PER_MB,
            getHeapMemoryUsagePercentage(),
            nonHeap.getUsed() / BYTES_PER_MB,
            nonHeap.getCommitted() / BYTES_PER_MB
        );
    }
}

3.2 OomAlertService - 預(yù)警核心邏輯

核心職責(zé):定時檢查內(nèi)存狀態(tài),觸發(fā)預(yù)警通知,管理冷卻機制。

package com.example.monitor.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.locks.ReentrantLock;

/**
 * OOM預(yù)警服務(wù)
 * 
 * <p>負(fù)責(zé)定時檢查內(nèi)存狀態(tài),當(dāng)內(nèi)存使用率超過閾值時觸發(fā)預(yù)警。
 * 支持多渠道通知(日志、控制臺、郵件),并具備冷卻機制避免頻繁報警。
 * 
 * <p><b>核心特性:</b>
 * <ul>
 *   <li>定時檢查:基于Spring @Scheduled實現(xiàn),默認(rèn)30秒檢查一次</li>
 *   <li>智能冷卻:觸發(fā)預(yù)警后進(jìn)入冷卻期,避免消息轟炸</li>
 *   <li>自動恢復(fù):內(nèi)存使用率下降后自動退出冷卻狀態(tài)</li>
 *   <li>多渠道通知:日志ERROR級別 + 控制臺高亮 + 郵件通知</li>
 * </ul>
 * 
 * @author 技術(shù)團隊
 * @since 1.0
 */
@Service
@ConditionalOnProperty(name = "monitor.oom.enabled", havingValue = "true", matchIfMissing = true)
public class OomAlertService {

    private static final Logger logger = LoggerFactory.getLogger(OomAlertService.class);
    
    /**
     * 預(yù)編譯的日期時間格式化器,避免重復(fù)創(chuàng)建對象
     */
    private static final DateTimeFormatter DATE_TIME_FORMATTER = 
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    
    /**
     * 退出冷卻狀態(tài)的緩沖區(qū),避免在閾值附近頻繁切換
     */
    private static final double COOLDOWN_EXIT_BUFFER = 5.0;
    
    // ==================== 配置參數(shù) ====================
    
    /**
     * OOM預(yù)警閾值(百分比)
     * 
     * <p>當(dāng)堆內(nèi)存使用率達(dá)到此值時觸發(fā)預(yù)警。
     * 推薦值:開發(fā)環(huán)境90%,測試環(huán)境85%,生產(chǎn)環(huán)境80%。
     */
    @Value("${monitor.oom.threshold:85.0}")
    private double oomThreshold;
    
    /**
     * 內(nèi)存檢查間隔(毫秒)
     * 
     * <p>定時任務(wù)的執(zhí)行頻率,默認(rèn)30秒一次。
     * 生產(chǎn)環(huán)境建議30-60秒,過于頻繁會增加系統(tǒng)開銷。
     */
    @Value("${monitor.oom.check-interval:30000}")
    private long checkInterval;
    
    /**
     * 預(yù)警冷卻時間(毫秒)
     * 
     * <p>觸發(fā)一次預(yù)警后,在冷卻期內(nèi)不再觸發(fā)新的預(yù)警。
     * 默認(rèn)5分鐘,避免同一問題頻繁報警。
     */
    @Value("${monitor.oom.cooldown-time:300000}")
    private long cooldownTime;
    
    /**
     * 是否啟用預(yù)警功能
     */
    @Value("${monitor.oom.alert-enabled:true}")
    private boolean alertEnabled;
    
    /**
     * 是否啟用郵件通知
     */
    @Value("${spring.mail.enabled:false}")
    private boolean mailEnabled;
    
    /**
     * 預(yù)警郵件接收人
     */
    @Value("${monitor.oom.alert-to:admin@example.com}")
    private String alertTo;
    
    // ==================== 依賴注入 ====================
    
    private final MemoryMonitorService memoryMonitorService;
    
    /**
     * 郵件發(fā)送器(延遲注入)
     */
    @org.springframework.context.annotation.Lazy
    private JavaMailSender mailSender;
    
    // ==================== 狀態(tài)管理 ====================
    
    /**
     * 上次觸發(fā)預(yù)警的時間戳
     */
    private volatile long lastAlertTime = 0;
    
    /**
     * 是否處于冷卻狀態(tài)
     */
    private volatile boolean isInCooldown = false;
    
    /**
     * 可重入鎖,用于保證冷卻狀態(tài)的原子性切換
     */
    private final ReentrantLock cooldownLock = new ReentrantLock();

    public OomAlertService(MemoryMonitorService memoryMonitorService) {
        this.memoryMonitorService = memoryMonitorService;
    }

    @PostConstruct
    public void init() {
        validateConfig();
        logger.info("OOM監(jiān)控初始化 - 閾值: {}%, 檢查間隔: {}ms, 冷卻時間: {}ms, 預(yù)警啟用: {}, 郵件啟用: {}",
            oomThreshold, checkInterval, cooldownTime, alertEnabled, mailEnabled);
    }

    /**
     * 配置參數(shù)校驗
     */
    private void validateConfig() {
        if (oomThreshold < 0 || oomThreshold > 100) {
            throw new IllegalArgumentException(
                String.format("閾值必須在[0, 100]范圍內(nèi),當(dāng)前值: %.2f", oomThreshold));
        }
        if (checkInterval <= 0) {
            throw new IllegalArgumentException(
                String.format("檢查間隔必須大于0,當(dāng)前值: %d", checkInterval));
        }
        if (cooldownTime <= 0) {
            throw new IllegalArgumentException(
                String.format("冷卻時間必須大于0,當(dāng)前值: %d", cooldownTime));
        }
    }

    /**
     * 定時檢查內(nèi)存狀態(tài)
     */
    @Scheduled(fixedRateString = "${monitor.oom.check-interval:30000}")
    public void checkMemoryStatus() {
        if (!alertEnabled) {
            return;
        }

        double currentUsage = memoryMonitorService.getHeapMemoryUsagePercentage();
        
        if (logger.isDebugEnabled()) {
            logger.debug("當(dāng)前內(nèi)存使用率: {:.2f}%, 閾值: {}%", currentUsage, oomThreshold);
        }

        // 檢查是否觸發(fā)預(yù)警
        if (memoryMonitorService.isOomRisk(oomThreshold)) {
            handleOomAlert(currentUsage);
        } else if (isInCooldown) {
            // 內(nèi)存使用率低于閾值,檢查是否退出冷卻狀態(tài)
            checkCooldownExit(currentUsage);
        }
    }

    /**
     * 檢查是否可以退出冷卻狀態(tài)
     */
    private void checkCooldownExit(double currentUsage) {
        if (currentUsage < oomThreshold - COOLDOWN_EXIT_BUFFER) {
            cooldownLock.lock();
            try {
                // 雙重檢查:避免并發(fā)修改
                if (isInCooldown && currentUsage < oomThreshold - COOLDOWN_EXIT_BUFFER) {
                    isInCooldown = false;
                    logger.info("內(nèi)存使用率已恢復(fù)正常({:.2f}%),退出預(yù)警冷卻狀態(tài)", currentUsage);
                }
            } finally {
                cooldownLock.unlock();
            }
        }
    }

    /**
     * 處理OOM預(yù)警
     */
    private void handleOomAlert(double currentUsage) {
        long currentTime = System.currentTimeMillis();
        
        // 檢查是否在冷卻期
        if (isInCooldown) {
            if (currentTime - lastAlertTime >= cooldownTime) {
                cooldownLock.lock();
                try {
                    // 雙重檢查
                    if (isInCooldown && currentTime - lastAlertTime >= cooldownTime) {
                        sendAlert(currentUsage, currentTime);
                    } else {
                        logger.warn("OOM預(yù)警處于冷卻期,跳過本次通知");
                    }
                } finally {
                    cooldownLock.unlock();
                }
            } else {
                logger.debug("OOM預(yù)警處于冷卻期,剩余時間: {}ms", 
                    cooldownTime - (currentTime - lastAlertTime));
            }
        } else {
            sendAlert(currentUsage, currentTime);
        }
    }

    /**
     * 發(fā)送預(yù)警
     */
    private void sendAlert(double currentUsage, long currentTime) {
        // 設(shè)置冷卻狀態(tài)
        isInCooldown = true;
        lastAlertTime = currentTime;
        
        // 構(gòu)建預(yù)警消息
        String alertMessage = buildAlertMessage(currentUsage);
        
        // 多渠道預(yù)警
        if (logger.isErrorEnabled()) {
            logger.error("{}", alertMessage);
        }
        consoleAlert(alertMessage);
        
        if (mailEnabled) {
            sendEmailAlertAsync(alertMessage);
        }
    }

    /**
     * 構(gòu)建預(yù)警消息
     */
    private String buildAlertMessage(double currentUsage) {
        String timestamp = LocalDateTime.now().format(DATE_TIME_FORMATTER);
        
        return String.format(
            "【OOM預(yù)警】%s\n" +
            "===========================\n" +
            "??  內(nèi)存使用率過高!\n" +
            "===========================\n" +
            "當(dāng)前使用率: %.2f%%\n" +
            "預(yù)警閾值: %.2f%%\n" +
            "內(nèi)存詳情: %s\n" +
            "===========================\n" +
            "建議立即檢查應(yīng)用狀態(tài)!\n",
            timestamp,
            currentUsage,
            oomThreshold,
            memoryMonitorService.getMemoryInfo()
        );
    }

    /**
     * 控制臺預(yù)警(開發(fā)環(huán)境快速響應(yīng))
     */
    private void consoleAlert(String message) {
        System.err.println("\n========================================");
        System.err.println(message);
        System.err.println("========================================\n");
    }

    /**
     * 異步發(fā)送郵件預(yù)警
     */
    private void sendEmailAlertAsync(final String message) {
        Thread emailThread = new Thread(() -> {
            try {
                if (mailSender == null) {
                    logger.warn("郵件發(fā)送器未初始化,跳過郵件通知");
                    return;
                }
                
                SimpleMailMessage mailMessage = new SimpleMailMessage();
                mailMessage.setTo(alertTo);
                mailMessage.setSubject("【緊急】Spring Boot應(yīng)用OOM預(yù)警");
                mailMessage.setText(message);
                
                mailSender.send(mailMessage);
                logger.info("OOM預(yù)警郵件已發(fā)送至: {}", alertTo);
            } catch (Exception e) {
                logger.error("發(fā)送OOM預(yù)警郵件失敗", e);
            }
        }, "oom-alert-email-sender");
        
        emailThread.setDaemon(true);
        emailThread.start();
    }

    /**
     * 手動觸發(fā)一次內(nèi)存檢查(用于測試)
     */
    public void manualCheck() {
        logger.info("手動觸發(fā)內(nèi)存檢查");
        checkMemoryStatus();
    }

    /**
     * 重置冷卻狀態(tài)(用于測試或緊急情況)
     */
    public void resetCooldown() {
        cooldownLock.lock();
        try {
            isInCooldown = false;
            lastAlertTime = 0;
            logger.info("冷卻狀態(tài)已重置");
        } finally {
            cooldownLock.unlock();
        }
    }

    /**
     * 獲取當(dāng)前冷卻狀態(tài)(用于監(jiān)控)
     */
    public boolean isInCooldown() {
        return isInCooldown;
    }

    /**
     * 獲取剩余冷卻時間(用于監(jiān)控)
     */
    public long getRemainingCooldownTime() {
        if (!isInCooldown) {
            return 0;
        }
        long elapsed = System.currentTimeMillis() - lastAlertTime;
        return Math.max(0, cooldownTime - elapsed);
    }
}

3.3 MonitorConfig - 配置管理

package com.example.monitor.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

/**
 * 監(jiān)控配置類
 * 
 * <p>負(fù)責(zé)啟用定時任務(wù),支持通過配置控制監(jiān)控功能的開關(guān)。
 * 
 * @author 技術(shù)團隊
 * @since 1.0
 */
@Configuration
@EnableScheduling
@ConditionalOnProperty(
    name = "monitor.oom.enabled", 
    havingValue = "true", 
    matchIfMissing = true
)
public class MonitorConfig {
    // 配置類,啟用定時任務(wù)
}

3.4 OomMonitorController - REST接口 暴露

package com.example.monitor.controller;

import com.example.monitor.service.MemoryMonitorService;
import com.example.monitor.service.OomAlertService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

/**
 * OOM監(jiān)控接口
 * 
 * <p>提供RESTful API,支持手動觸發(fā)檢查和實時查詢內(nèi)存使用情況。
 * 
 * @author 技術(shù)團隊
 * @since 1.0
 */
@RestController
@RequestMapping("/api/monitor")
@Tag(name = "OOM監(jiān)控", description = "內(nèi)存監(jiān)控與預(yù)警接口")
@ConditionalOnProperty(name = "monitor.oom.enabled", havingValue = "true", matchIfMissing = true)
public class OomMonitorController {

    @Autowired
    private MemoryMonitorService memoryMonitorService;

    @Autowired
    private OomAlertService oomAlertService;

    /**
     * 獲取當(dāng)前內(nèi)存使用情況
     */
    @GetMapping("/memory")
    @Operation(summary = "獲取內(nèi)存使用情況", description = "返回詳細(xì)的堆內(nèi)存和非堆內(nèi)存信息")
    public ResponseEntity<Map<String, Object>> getMemoryStatus() {
        Map<String, Object> result = new HashMap<>();
        
        Map<String, Object> heap = new HashMap<>();
        heap.put("used", memoryMonitorService.getHeapMemoryUsage().getUsed() / (1024L * 1024L));
        heap.put("committed", memoryMonitorService.getHeapMemoryUsage().getCommitted() / (1024L * 1024L));
        heap.put("max", memoryMonitorService.getHeapMemoryUsage().getMax() / (1024L * 1024L));
        heap.put("usageRate", memoryMonitorService.getHeapMemoryUsagePercentage());
        
        Map<String, Object> nonHeap = new HashMap<>();
        nonHeap.put("used", memoryMonitorService.getNonHeapMemoryUsage().getUsed() / (1024L * 1024L));
        nonHeap.put("committed", memoryMonitorService.getNonHeapMemoryUsage().getCommitted() / (1024L * 1024L));
        
        result.put("heap", heap);
        result.put("nonHeap", nonHeap);
        
        return ResponseEntity.ok(result);
    }

    /**
     * 獲取內(nèi)存使用率
     */
    @GetMapping("/memory/usage")
    @Operation(summary = "獲取內(nèi)存使用率", description = "返回堆內(nèi)存使用率百分比")
    public ResponseEntity<Map<String, Object>> getMemoryUsage() {
        double usage = memoryMonitorService.getHeapMemoryUsagePercentage();
        double roundedUsage = Math.round(usage * 100) / 100.0;
        
        Map<String, Object> result = new HashMap<>();
        result.put("usageRate", roundedUsage);
        result.put("status", getMemoryStatus(roundedUsage));
        
        return ResponseEntity.ok(result);
    }

    /**
     * 手動觸發(fā)一次內(nèi)存檢查
     */
    @GetMapping("/check")
    @Operation(summary = "手動觸發(fā)內(nèi)存檢查", description = "立即執(zhí)行一次內(nèi)存檢查并觸發(fā)預(yù)警(如需要)")
    public ResponseEntity<Map<String, Object>> manualCheck() {
        oomAlertService.manualCheck();
        
        Map<String, Object> result = new HashMap<>();
        result.put("message", "內(nèi)存檢查已觸發(fā),請查看日志輸出");
        result.put("timestamp", System.currentTimeMillis());
        
        return ResponseEntity.ok(result);
    }

    /**
     * 獲取冷卻狀態(tài)
     */
    @GetMapping("/cooldown")
    @Operation(summary = "獲取冷卻狀態(tài)", description = "返回預(yù)警冷卻狀態(tài)和剩余冷卻時間")
    public ResponseEntity<Map<String, Object>> getCooldownStatus() {
        Map<String, Object> result = new HashMap<>();
        result.put("isInCooldown", oomAlertService.isInCooldown());
        result.put("remainingTime", oomAlertService.getRemainingCooldownTime());
        
        return ResponseEntity.ok(result);
    }

    /**
     * 重置冷卻狀態(tài)
     */
    @GetMapping("/cooldown/reset")
    @Operation(summary = "重置冷卻狀態(tài)", description = "強制重置預(yù)警冷卻狀態(tài)")
    public ResponseEntity<Map<String, Object>> resetCooldown() {
        oomAlertService.resetCooldown();
        
        Map<String, Object> result = new HashMap<>();
        result.put("message", "冷卻狀態(tài)已重置");
        result.put("timestamp", System.currentTimeMillis());
        
        return ResponseEntity.ok(result);
    }

    private String getMemoryStatus(double usageRate) {
        if (usageRate >= 90) {
            return "CRITICAL";
        } else if (usageRate >= 80) {
            return "WARNING";
        } else if (usageRate >= 60) {
            return "NORMAL";
        } else {
            return "GOOD";
        }
    }
}

4. 配置與部署

4.1 Maven依賴配置

<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Actuator(必須) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!-- Mail(可選,用于郵件預(yù)警) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
        <optional>true</optional>
    </dependency>
    <!-- Swagger(可選,用于API文檔) -->
    <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
        <version>2.0.2</version>
    </dependency>
</dependencies>

4.2 完整配置文件

server:
  port: 8080
spring:
  application:
    name: oom-monitor-demo
  # ==================== 郵件配置 ====================
  mail:
    # 是否啟用郵件預(yù)警(總開關(guān))
    enabled: false
    # SMTP服務(wù)器配置
    host: smtp.example.com
    port: 587
    username: your-email@example.com
    password: your-password
    # SMTP協(xié)議配置
    properties:
      mail:
        smtp:
          auth: true
          starttls:
            enabled: true
          connectiontimeout: 5000
          timeout: 10000
          writetimeout: 10000
# ==================== OOM監(jiān)控配置 ====================
monitor:
  oom:
    # 是否啟用OOM監(jiān)控(總開關(guān))
    enabled: true
    # 內(nèi)存使用率閾值(百分比),超過此值觸發(fā)預(yù)警
    # 推薦值:開發(fā)環(huán)境90%、測試環(huán)境85%、生產(chǎn)環(huán)境80%
    threshold: 85.0
    # 內(nèi)存檢查間隔(毫秒),默認(rèn)30秒
    # 生產(chǎn)環(huán)境建議30-60秒
    check-interval: 30000
    # 預(yù)警冷卻時間(毫秒),防止頻繁預(yù)警
    # 默認(rèn)5分鐘,可根據(jù)實際情況調(diào)整
    cooldown-time: 300000
    # 是否啟用預(yù)警通知
    alert-enabled: true
    # 預(yù)警郵件接收人(多個用逗號分隔)
    alert-to: admin@example.com,ops@example.com
# ==================== Spring Task調(diào)度配置 ====================
spring:
  task:
    scheduling:
      # 調(diào)度線程池大小
      pool:
        size: 1
      # 線程名稱前綴
      thread-name-prefix: oom-monitor-
      # 是否等待任務(wù)執(zhí)行完成后再關(guān)閉應(yīng)用
      shutdown:
        await-termination: true
        await-termination-period: 60s
# ==================== Actuator配置 ====================
# 用于Prometheus集成
management:
  endpoints:
    web:
      # 暴露的端點列表
      exposure:
        include: health,info,metrics,prometheus
      # Actuator基礎(chǔ)路徑
      base-path: /actuator
  endpoint:
    health:
      # 健康檢查詳情展示級別
      show-details: when-authorized
      # 顯示組件詳情
      show-components: always
  metrics:
    export:
      prometheus:
        # 是否啟用Prometheus指標(biāo)導(dǎo)出
        enabled: true
    # 指標(biāo)標(biāo)簽
    tags:
      application: ${spring.application.name}
      environment: ${spring.profiles.active:default}
# ==================== 日志配置 ====================
logging:
  level:
    root: INFO
    # 監(jiān)控相關(guān)日志級別
    com.example.monitor: INFO
  pattern:
    # 控制臺日志格式
    console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
  # 日志文件配置(可選)
  file:
    name: logs/oom-monitor.log
    max-size: 10MB
    max-history: 7
    total-size-cap: 100MB

4.3 JVM參數(shù)調(diào)優(yōu)

生產(chǎn)環(huán)境推薦配置

java -jar app.jar \
  # 堆內(nèi)存配置
  -Xms512m \
  -Xmx1024m \
  -Xmn256m \
  # OOM時的自動Dump
  -XX:+HeapDumpOnOutOfMemoryError \
  -XX:HeapDumpPath=/logs/heapdump.hprof \
  # GC日志
  -XX:+PrintGCDetails \
  -XX:+PrintGCDateStamps \
  -Xloggc:/logs/gc.log \
  # Metaspace配置(防止類加載溢出)
  -XX:MetaspaceSize=128m \
  -XX:MaxMetaspaceSize=256m \
  # 性能優(yōu)化
  -XX:+UseG1GC \
  -XX:MaxGCPauseMillis=200

5. Prometheus企業(yè)級集成

5.1 Actuator配置

Spring Boot Actuator已內(nèi)置Prometheus支持,只需配置端點暴露:

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  metrics:
    export:
      prometheus:
        enabled: true
    tags:
      application: ${spring.application.name}

訪問以下端點查看指標(biāo):

  • /actuator/prometheus - Prometheus格式的指標(biāo)數(shù)據(jù)
  • /actuator/metrics/jvm.memory.used - JVM內(nèi)存使用量
  • /actuator/metrics/jvm.memory.max - JVM最大內(nèi)存

5.2 Prometheus配置

prometheus.yml

global:
  scrape_interval: 15s
  evaluation_interval: 15s
scrape_configs:
  - job_name: 'spring-boot-oom-monitor'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['localhost:8080']
        labels:
          application: 'oom-monitor-demo'
          environment: 'production'
# 引用告警規(guī)則文件
rule_files:
  - 'prometheus_alert_rules.yml'

5.3 告警規(guī)則配置

prometheus_alert_rules.yml

groups:
  - name: oom_alerts
    interval: 30s
    rules:
      # 堆內(nèi)存使用率過高預(yù)警(警告級別)
      - alert: HighHeapMemoryUsage
        expr: |
          (
            jvm_memory_used_bytes{area="heap"} / 
            jvm_memory_max_bytes{area="heap"}
          ) * 100 > 85
        for: 2m
        labels:
          severity: warning
          team: ops
        annotations:
          summary: "JVM堆內(nèi)存使用率過高"
          description: "{{ $labels.instance }} 應(yīng)用 {{ $labels.application }} 堆內(nèi)存使用率達(dá)到 {{ $value | humanizePercentage }},請及時排查"
      # 堆內(nèi)存使用率嚴(yán)重預(yù)警(嚴(yán)重級別)
      - alert: CriticalHeapMemoryUsage
        expr: |
          (
            jvm_memory_used_bytes{area="heap"} / 
            jvm_memory_max_bytes{area="heap"}
          ) * 100 > 95
        for: 1m
        labels:
          severity: critical
          team: ops
        annotations:
          summary: "JVM堆內(nèi)存使用率嚴(yán)重過高!"
          description: "{{ $labels.instance }} 應(yīng)用 {{ $labels.application }} 堆內(nèi)存使用率達(dá)到 {{ $value | humanizePercentage }},即將發(fā)生OOM,請立即處理!"
      # 非堆內(nèi)存使用率過高
      - alert: HighNonHeapMemoryUsage
        expr: |
          (
            jvm_memory_used_bytes{area="nonheap"} / 
            jvm_memory_max_bytes{area="nonheap"}
          ) * 100 > 90
        for: 5m
        labels:
          severity: warning
          team: ops
        annotations:
          summary: "JVM非堆內(nèi)存使用率過高"
          description: "{{ $labels.instance }} 應(yīng)用 {{ $labels.application }} 非堆內(nèi)存使用率達(dá)到 {{ $value | humanizePercentage }},可能存在Metaspace溢出風(fēng)險"
      # GC頻率過高預(yù)警
      - alert: HighGcFrequency
        expr: |
          rate(jvm_gc_pause_seconds_count[5m]) > 10
        for: 5m
        labels:
          severity: warning
          team: ops
        annotations:
          summary: "GC頻率過高"
          description: "{{ $labels.instance }} 應(yīng)用 {{ $labels.application }} 最近5分鐘GC頻率達(dá)到 {{ $value }}/秒,可能存在內(nèi)存泄漏"
      # GC耗時過長預(yù)警
      - alert: LongGcPause
        expr: |
          rate(jvm_gc_pause_seconds_sum[5m]) / rate(jvm_gc_pause_seconds_count[5m]) > 0.5
        for: 5m
        labels:
          severity: warning
          team: ops
        annotations:
          summary: "GC平均耗時過長"
          description: "{{ $labels.instance }} 應(yīng)用 {{ $labels.application }} 最近5分鐘GC平均耗時達(dá)到 {{ $value }}秒,嚴(yán)重影響應(yīng)用性能"
      # Full GC頻繁預(yù)警
      - alert: FrequentFullGc
        expr: |
          increase(jvm_gc_pause_seconds_count{action="end of major GC"}[10m]) > 3
        for: 2m
        labels:
          severity: warning
          team: ops
        annotations:
          summary: "Full GC頻繁發(fā)生"
          description: "{{ $labels.instance }} 應(yīng)用 {{ $labels.application }} 最近10分鐘發(fā)生了 {{ $value }} 次Full GC,可能存在內(nèi)存泄漏或堆內(nèi)存不足"

5.4 Grafana儀表盤

推薦使用現(xiàn)成的JVM監(jiān)控儀表盤:

  • Grafana Dashboard ID: 4701 (JVM (Micrometer))
  • 導(dǎo)入方式
    1. 登錄Grafana
    2. 進(jìn)入"Dashboards" → “Import”
    3. 輸入Dashboard ID: 4701
    4. 選擇Prometheus數(shù)據(jù)源
    5. 點擊Import

儀表盤包含的圖表

  • 堆內(nèi)存使用趨勢
  • 非堆內(nèi)存使用趨勢
  • GC頻率和耗時
  • 線程數(shù)變化
  • 類加載數(shù)量
  • 請求處理速率

6. 生產(chǎn)環(huán)境最佳實踐

6.1 閾值設(shè)置建議

不同環(huán)境的閾值設(shè)置應(yīng)有所區(qū)別:

環(huán)境閾值檢查間隔冷卻時間說明
開發(fā)環(huán)境90%30秒3分鐘容忍度高,快速發(fā)現(xiàn)即可
測試環(huán)境85%30秒5分鐘模擬生產(chǎn)環(huán)境
生產(chǎn)環(huán)境80%60秒10分鐘提前預(yù)警,避免誤報

6.2 部署檢查清單

部署前請確認(rèn)以下事項:

  • JVM參數(shù)已正確配置(-Xms、-Xmx、-XX:+HeapDumpOnOutOfMemoryError)
  • 日志目錄有寫權(quán)限(/logs)
  • 郵件SMTP配置正確(如啟用郵件預(yù)警)
  • Actuator端點已暴露(如對接Prometheus)
  • 防火墻允許Actuator端口訪問(默認(rèn)與管理端口相同)
  • 配置文件中的閾值和檢查間隔符合環(huán)境要求

6.3 安全建議

1. Actuator端點保護(hù)

生產(chǎn)環(huán)境必須保護(hù)Actuator端點:

management:
  endpoints:
    web:
      exposure:
        # 只暴露必要的端點
        include: health,metrics,prometheus
  endpoint:
    health:
      show-details: when-authorized  # 只對授權(quán)用戶顯示詳情

2. 郵件憑證管理

  • 不要將SMTP密碼硬編碼在配置文件中
  • 使用配置中心(如Nacos、Apollo)管理敏感信息
  • 使用環(huán)境變量傳遞憑證

7. 故障排查指南

7.1 常見問題排查

問題1:預(yù)警頻繁觸發(fā)

癥狀:內(nèi)存使用率在閾值附近波動,導(dǎo)致頻繁報警

原因

  • 閾值設(shè)置過低
  • 冷卻時間過短
  • 應(yīng)用本身存在內(nèi)存抖動

解決方案

  1. 提高預(yù)警閾值(如從85%調(diào)整到90%)
  2. 增加冷卻時間(如從5分鐘調(diào)整到10分鐘)
  3. 分析GC日志,優(yōu)化內(nèi)存分配策略

問題2:郵件發(fā)送失敗

癥狀:控制臺顯示"發(fā)送OOM預(yù)警郵件失敗"

原因

  • SMTP配置錯誤
  • 郵箱未開啟SMTP服務(wù)
  • 網(wǎng)絡(luò)防火墻阻止

解決方案

  1. 檢查SMTP配置(host、port、username、password)
  2. 確認(rèn)郵箱已開啟SMTP服務(wù)(如QQ郵箱需開啟"IMAP/SMTP服務(wù)")
  3. 檢查防火墻規(guī)則,確保能訪問SMTP服務(wù)器

問題3:監(jiān)控未生效

癥狀:啟動后無日志輸出,接口調(diào)用無響應(yīng)

原因

  • monitor.oom.enabled配置為false
  • 包路徑錯誤
  • @EnableScheduling未生效

解決方案

  1. 確認(rèn)配置文件中monitor.oom.enabled=true
  2. 檢查包路徑是否與項目包名一致
  3. 確認(rèn)MonitorConfig類被Spring掃描到

問題4:內(nèi)存使用率不準(zhǔn)確

癥狀:使用率顯示為0%或異常高

原因

  • JVM未設(shè)置-Xmx參數(shù)
  • JVM動態(tài)調(diào)整堆內(nèi)存大小
  • max值為-1或0

解決方案

  1. 設(shè)置固定的-Xmx參數(shù)(如-Xmx1024m
  2. 使用-Xms-Xmx相同,避免動態(tài)擴容
  3. 檢查JVM參數(shù)配置

7.2 HeapDump分析工具

1. Eclipse MAT (Memory Analyzer Tool)

  • 官網(wǎng):https://www.eclipse.org/mat/
  • 功能:分析Heap Dump文件,查找內(nèi)存泄漏
  • 使用方法
    1. 打開MAT
    2. File → Open Heap Dump
    3. 選擇.hprof文件
    4. 點擊"Leak Suspects"自動分析

2. VisualVM

  • 官網(wǎng):https://visualvm.github.io/
  • 功能:實時監(jiān)控和Heap Dump分析
  • 使用方法
    1. 啟動VisualVM
    2. 連接到Java進(jìn)程
    3. 點擊"Heap Dump"生成快照
    4. 分析大對象和GC Roots

3. JProfiler

  • 官網(wǎng):https://www.ej-technologies.com/products/jprofiler/overview
  • 功能:商業(yè)級性能分析工具
  • 特點:強大的內(nèi)存分析和CPU剖析能力

8. 總結(jié)

本文從技術(shù)原理到工程實踐,完整介紹了Spring Boot OOM監(jiān)測預(yù)警系統(tǒng)的構(gòu)建方法。核心要點回顧:

核心特性

  • ? 輕量級集成:純Spring Boot生態(tài),零侵入
  • ? 智能預(yù)警:多閾值、冷卻機制,避免誤報
  • ? 多渠道通知:日志 + 控制臺 + 郵件
  • ? 企業(yè)級友好:無縫對接Prometheus + Grafana

技術(shù)棧

  • 數(shù)據(jù)采集:JVM MBean (java.lang.management)
  • 定時任務(wù):Spring @Scheduled
  • 監(jiān)控端點:Spring Boot Actuator
  • 指標(biāo)采集:Micrometer + Prometheus
  • 可視化:Grafana

生產(chǎn)環(huán)境建議

  1. JVM參數(shù):必須配置-Xms、-Xmx、-XX:+HeapDumpOnOutOfMemoryError
  2. 閾值設(shè)置:生產(chǎn)環(huán)境推薦80%,提前預(yù)警
  3. 監(jiān)控擴展:關(guān)注GC頻率、耗時、Full GC次數(shù)
  4. 自動化運維:集成自動HeapDump、自動擴容
  5. 安全加固:保護(hù)Actuator端點,管理敏感憑證

以上就是基于SpringBoot從零構(gòu)建生產(chǎn)級OOM監(jiān)測預(yù)警系統(tǒng)的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot OOM監(jiān)測預(yù)警系統(tǒng)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java中MessageFormat的使用詳解

    Java中MessageFormat的使用詳解

    本文主要介紹了Java中MessageFormat的使用詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Java中Sentinel框架詳解

    Java中Sentinel框架詳解

    Sentinel是一個高可用、高擴展、高穩(wěn)定性的開源流量控制和熔斷降級框架,可以在分布式系統(tǒng)中實現(xiàn)實時的流量控制,防止系統(tǒng)因流量過大導(dǎo)致系統(tǒng)崩潰和服務(wù)降級,Sentinel面向所有的Java應(yīng)用,本文就給大家詳細(xì)介紹一下Java中Sentinel框架,需要的朋友可以參考下
    2023-06-06
  • knife4j?整合?springboot的過程詳解

    knife4j?整合?springboot的過程詳解

    這篇文章主要介紹了knife4j整合springboot的過程,本次整合springboot版本為2.3.12,本文通過圖文實例相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-09-09
  • java實現(xiàn)一個簡單TCPSocket聊天室功能分享

    java實現(xiàn)一個簡單TCPSocket聊天室功能分享

    這篇文章主要為大家分享了java實現(xiàn)的一個簡單TCPSocket聊天室功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-04-04
  • SpringBoot整合RabbitMQ實現(xiàn)通配符模式

    SpringBoot整合RabbitMQ實現(xiàn)通配符模式

    本文主要介紹了SpringBoot整合RabbitMQ實現(xiàn)通配符模式,包括依賴添加、配置、隊列與交換機聲明及綁定,生產(chǎn)者發(fā)送消息,兩個消費者分別接收并處理,驗證消息正確分發(fā)至不同隊列,感興趣的可以了解一下
    2025-06-06
  • 線程池之newCachedThreadPool可緩存線程池的實例

    線程池之newCachedThreadPool可緩存線程池的實例

    這篇文章主要介紹了線程池之newCachedThreadPool可緩存線程池的實例,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • 關(guān)于FastJson?long?溢出問題的小結(jié)

    關(guān)于FastJson?long?溢出問題的小結(jié)

    這篇文章主要介紹了關(guān)于FastJson?long?溢出問題的小結(jié),具有很好的參考價值,希望對大家有所幫助。
    2022-01-01
  • Springboot項目javax.validation使用方法詳解

    Springboot項目javax.validation使用方法詳解

    這篇文章主要介紹了Springboot項目javax.validation使用方法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-04-04
  • 企業(yè)級Kubernetes管理平臺Wayne功能特性介紹

    企業(yè)級Kubernetes管理平臺Wayne功能特性介紹

    這篇文章主要為大家介紹了企業(yè)級Kubernetes管理平臺Wayne的功能特性及架構(gòu)設(shè)計,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-02-02
  • Redis+Caffeine實現(xiàn)兩級緩存的教程

    Redis+Caffeine實現(xiàn)兩級緩存的教程

    這篇文章主要介紹了Redis+Caffeine實現(xiàn)兩級緩存的教程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-05-05

最新評論

综艺| 沽源县| 宁蒗| 台前县| 平潭县| 鱼台县| 湟中县| 泾川县| 陕西省| 绥棱县| 滦平县| 吴忠市| 赤水市| 安顺市| 应城市| 全州县| 绵阳市| 宁安市| 财经| 吴桥县| 林芝县| 武城县| 台北市| 屏山县| 永修县| 姚安县| 青冈县| 蕉岭县| 江西省| 顺平县| 石门县| 巨野县| 平南县| 平罗县| 乌拉特后旗| 河间市| 延津县| 崇左市| 临猗县| 大连市| 屯昌县|