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

SpringBoot實現QPS監(jiān)控的完整代碼

 更新時間:2025年12月10日 10:04:17   作者:悟空碼字  
本文介紹了QPS(每秒查詢率)的概念,并討論了三種監(jiān)控方案:簡易版(AOP攔截器)、專業(yè)版(Micrometer+Prometheus)和豪華版(SpringBootAdmin),每種方案都有詳細的實施步驟,文章還介紹了QPS統(tǒng)計的進階技巧,并強調了監(jiān)控系統(tǒng)的重要性,需要的朋友可以參考下

一、什么是QPS?—— 系統(tǒng)的"心跳頻率" 

想象一下你的系統(tǒng)就像一個忙碌的外賣小哥,QPS(Query Per Second)就是他每秒能送多少份外賣!如果小哥每秒只能送1單,那估計顧客早就餓暈在廁所了;要是每秒能送100單,那他絕對是"閃電俠"附體!

正常系統(tǒng)的QPS就像人的心跳:

  • 60-100 QPS:健康小伙子,心跳平穩(wěn)
  • 100-1000 QPS:健身達人,有點小激動
  • 1000+ QPS:跑馬拉松呢!快喘口氣!
  • 10000+ QPS:這貨是打了雞血吧?

二、方案大比拼——給系統(tǒng)裝上"智能手環(huán)"

方案1:簡易版手環(huán)(AOP攔截器)

適合小項目,就像給系統(tǒng)戴個手環(huán)

@Slf4j
@Aspect
@Component
public class QpsMonitorAspect {
    
    // 用ConcurrentHashMap存計數器,線程安全!
    private final ConcurrentHashMap<String, AtomicLong> counterMap = new ConcurrentHashMap<>();
    private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    
    @PostConstruct
    public void init() {
        log.info("QPS監(jiān)控龜龜已啟動,開始慢慢爬...");
        // 每秒統(tǒng)計一次,像烏龜一樣穩(wěn)定
        scheduler.scheduleAtFixedRate(this::printQps, 0, 1, TimeUnit.SECONDS);
    }
    
    @Around("@annotation(org.springframework.web.bind.annotation.GetMapping) || " +
            "@annotation(org.springframework.web.bind.annotation.PostMapping)")
    public Object countQps(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().toShortString();
        
        // 計數器自增,像小松鼠囤松果一樣積極
        counterMap.computeIfAbsent(methodName, k -> new AtomicLong(0))
                 .incrementAndGet();
        
        long start = System.currentTimeMillis();
        try {
            return joinPoint.proceed();
        } finally {
            long cost = System.currentTimeMillis() - start;
            // 順便記錄一下響應時間,看看系統(tǒng)是不是"老了腿腳慢"
            if (cost > 1000) {
                log.warn("方法 {} 執(zhí)行了 {}ms,比蝸牛還慢!", methodName, cost);
            }
        }
    }
    
    private void printQps() {
        if (counterMap.isEmpty()) {
            log.info("系統(tǒng)在睡大覺,沒有請求...");
            return;
        }
        
        StringBuilder sb = new StringBuilder("\n========== QPS報告 ==========\n");
        counterMap.forEach((method, counter) -> {
            long qps = counter.getAndSet(0); // 重置計數器
            String status = "";
            if (qps > 1000) status = "";
            if (qps > 5000) status = "";
            
            sb.append(String.format("%s %-40s : %d QPS%n", 
                    status, method, qps));
        });
        sb.append("================================");
        log.info(sb.toString());
    }
}

方案2:專業(yè)版體檢儀(Micrometer + Prometheus)

適合大項目,就像給系統(tǒng)做全面體檢

@Configuration
public class MetricsConfig {
    
    @Bean
    public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
        return registry -> {
            registry.config().commonTags("application", "my-awesome-app");
            log.info("系統(tǒng)體檢中心開業(yè)啦!歡迎隨時來檢查身體~");
        };
    }
}

@Service
public class OrderService {
    
    private final Counter orderCounter;
    private final Timer orderTimer;
    private final MeterRegistry meterRegistry;
    
    public OrderService(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
        
        // 創(chuàng)建訂單計數器,像收銀機一樣"叮叮叮"
        this.orderCounter = Counter.builder("order.count")
                .description("訂單數量統(tǒng)計")
                .tag("type", "create")
                .register(meterRegistry);
        
        // 創(chuàng)建訂單耗時計時器
        this.orderTimer = Timer.builder("order.process.time")
                .description("訂單處理時間")
                .register(meterRegistry);
    }
    
    public Order createOrder(OrderDTO dto) {
        // 記錄方法執(zhí)行時間
        return orderTimer.record(() -> {
            log.debug("正在打包訂單,請稍候...");
            
            // 業(yè)務邏輯...
            Order order = doCreateOrder(dto);
            
            // 訂單創(chuàng)建成功,計數器+1
            orderCounter.increment();
            
            // 動態(tài)QPS統(tǒng)計(最近1分鐘)
            double qps = meterRegistry.get("order.count")
                    .counter()
                    .measure()
                    .stream()
                    .findFirst()
                    .map(Measurement::getValue)
                    .orElse(0.0) / 60.0;
            
            if (qps > 100) {
                log.warn("訂單處理太快了!當前QPS: {}/s,考慮加點運費?", qps);
            }
            
            return order;
        });
    }
    
    // 動態(tài)查看QPS的API
    @GetMapping("/metrics/qps")
    public Map<String, Object> getRealTimeQps() {
        Map<String, Object> metrics = new HashMap<>();
        
        // 收集所有接口的QPS
        meterRegistry.getMeters().forEach(meter -> {
            String meterName = meter.getId().getName();
            if (meterName.contains(".count")) {
                double qps = meter.counter().count() / 60.0; // 轉換為每秒
                metrics.put(meterName, String.format("%.2f QPS", qps));
                
                // 添加表情包增強可視化效果
                String emoji = "";
                if (qps > 100) emoji = "";
                if (qps > 500) emoji = "";
                metrics.put(meterName + "_emoji", emoji);
            }
        });
        
        metrics.put("report_time", LocalDateTime.now());
        metrics.put("message", "系統(tǒng)當前狀態(tài)良好,吃嘛嘛香!");
        
        return metrics;
    }
}

方案3:豪華版監(jiān)控大屏(Spring Boot Admin)

給老板看的,必須高大上!

# application.yml
spring:
  boot:
    admin:
      client:
        url: http://localhost:9090  # Admin Server地址
        instance:
          name: "青龍系統(tǒng)"
          metadata:
            owner: "碼農小張"
            department: "爆肝事業(yè)部"

management:
  endpoints:
    web:
      exposure:
        include: "*"  # 暴露所有端點,不穿"隱身衣"
  metrics:
    export:
      prometheus:
        enabled: true
  endpoint:
    health:
      show-details: ALWAYS
@RestController
@Slf4j
public class QpsDashboardController {
    
    @GetMapping("/dashboard/qps")
    public String qpsDashboard() {
        // 模擬從各個服務收集QPS數據
        Map<String, Double> serviceQps = getClusterQps();
        
        // 生成ASCII藝術報表
        StringBuilder dashboard = new StringBuilder();
        dashboard.append("\n");
        dashboard.append("╔══════════════════════════════════════════╗\n");
        dashboard.append("║        系統(tǒng)QPS監(jiān)控大屏                     ║\n");
        dashboard.append("╠══════════════════════════════════════════╣\n");
        
        serviceQps.forEach((service, qps) -> {
            // 生成進度條
            int bars = (int) Math.min(qps / 10, 50);
            String progressBar = "█".repeat(bars) + 
                               "?".repeat(50 - bars);
            
            String status = "正常";
            if (qps > 500) status = "警告";
            if (qps > 1000) status = "緊急";
            
            dashboard.append(String.format("║ %-15s : %-30s ║\n", 
                    service, progressBar));
            dashboard.append(String.format("║   %6.1f QPS %-20s        ║\n", 
                    qps, status));
        });
        
        dashboard.append("╚══════════════════════════════════════════╝\n");
        
        // 添加系統(tǒng)健康建議
        dashboard.append("\n 系統(tǒng)建議:\n");
        double maxQps = serviceQps.values().stream().max(Double::compare).orElse(0.0);
        if (maxQps < 50) {
            dashboard.append("   系統(tǒng)有點閑,可以考慮接點私活~ \n");
        } else if (maxQps > 1000) {
            dashboard.append("   系統(tǒng)快冒煙了!快加機器!\n");
        } else {
            dashboard.append("   狀態(tài)完美,繼續(xù)保持!\n");
        }
        
        return dashboard.toString();
    }
    
    // 定時推送QPS警告
    @Scheduled(fixedRate = 60000)
    public void checkQpsAlert() {
        Map<String, Double> currentQps = getClusterQps();
        
        currentQps.forEach((service, qps) -> {
            if (qps > 1000) {
                log.error("救命!{}服務QPS爆表了:{},快看看是不是被爬了!", 
                         service, qps);
                // 這里可以接入釘釘/企業(yè)微信告警
                sendAlertToDingTalk(service, qps);
            }
        });
    }
    
    private void sendAlertToDingTalk(String service, double qps) {
        String message = String.format(
            "{\"msgtype\": \"text\", \"text\": {\"content\": \"%s服務QPS異常:%.1f,快去看看吧!\"}}",
            service, qps
        );
        // 調用釘釘webhook
        log.warn("已發(fā)送釘釘告警:{}", message);
    }
}

三、方案詳細實施步驟

方案1實施步驟(簡易版):

  1. 添加依賴:給你的pom.xml來點"維生素"
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 啟用AOP:在主類上貼個"創(chuàng)可貼"
@SpringBootApplication
@EnableAspectJAutoProxy  // 啟用AOP魔法
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
        System.out.println("QPS監(jiān)控小雞破殼而出!");
    }
}
  1. 創(chuàng)建切面類:如上文的QpsMonitorAspect
  2. 測試一下:瘋狂刷新接口,看看控制臺輸出

方案2實施步驟(專業(yè)版):

  1. 添加全家桶依賴
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-core</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  1. 配置application.yml
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  metrics:
    export:
      prometheus:
        enabled: true
    tags:
      application: ${spring.application.name}
  endpoint:
    metrics:
      enabled: true
  1. 訪問監(jiān)控數據
http://localhost:8080/actuator/metrics  # 查看所有指標
http://localhost:8080/actuator/prometheus  # Prometheus格式

方案3實施步驟(豪華版):

  1. 搭建Spring Boot Admin Server
@SpringBootApplication
@EnableAdminServer
public class AdminServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(AdminServerApplication.class, args);
        System.out.println("監(jiān)控大屏已就位,陛下請檢閱!");
    }
}
  1. 客戶端配置:如上文yml配置
  2. 訪問Admin UIhttp://localhost:9090

四、QPS統(tǒng)計的進階技巧

1. 滑動窗口統(tǒng)計(最近N秒的QPS)

public class SlidingWindowQpsCounter {
    // 用環(huán)形隊列實現滑動窗口
    private final LinkedList<Long> timestamps = new LinkedList<>();
    private final int windowSeconds;
    
    public SlidingWindowQpsCounter(int windowSeconds) {
        this.windowSeconds = windowSeconds;
        log.info("創(chuàng)建滑動窗口監(jiān)控,窗口大?。簕}秒", windowSeconds);
    }
    
    public synchronized void hit() {
        long now = System.currentTimeMillis();
        timestamps.add(now);
        // 移除窗口外的記錄
        while (!timestamps.isEmpty() && 
               now - timestamps.getFirst() > windowSeconds * 1000) {
            timestamps.removeFirst();
        }
    }
    
    public double getQps() {
        return timestamps.size() / (double) windowSeconds;
    }
}

2. 分位數統(tǒng)計(P90/P95/P99響應時間)

@Bean
public MeterRegistryCustomizer<MeterRegistry> addQuantiles() {
    return registry -> {
        DistributionStatisticConfig config = DistributionStatisticConfig.builder()
                .percentiles(0.5, 0.9, 0.95, 0.99)  // 50%, 90%, 95%, 99%
                .percentilePrecision(2)
                .build();
        
        registry.config().meterFilter(
            new MeterFilter() {
                @Override
                public DistributionStatisticConfig configure(
                    Meter.Id id, DistributionStatisticConfig config) {
                    if (id.getName().contains(".timer")) {
                        return config.merge(DistributionStatisticConfig.builder()
                                .percentiles(0.5, 0.9, 0.95, 0.99)
                                .build());
                    }
                    return config;
                }
            }
        );
        
        log.info("分位數統(tǒng)計已啟用,準備精準打擊慢查詢!");
    };
}

3. 基于QPS的自動熔斷

@Component
public class AdaptiveCircuitBreaker {
    private volatile boolean circuitOpen = false;
    private double currentQps = 0;
    
    @Scheduled(fixedRate = 1000)
    public void monitorAndAdjust() {
        // 獲取當前QPS
        currentQps = calculateCurrentQps();
        
        if (circuitOpen && currentQps < 100) {
            circuitOpen = false;
            log.info("熔斷器關閉,系統(tǒng)恢復供電!當前QPS: {}", currentQps);
        } else if (!circuitOpen && currentQps > 1000) {
            circuitOpen = true;
            log.error("熔斷器觸發(fā)!QPS過高: {},系統(tǒng)進入保護模式", currentQps);
        }
        
        // 動態(tài)調整線程池大小
        adjustThreadPool(currentQps);
    }
    
    private void adjustThreadPool(double qps) {
        int suggestedSize = (int) (qps * 0.5);  // 經驗公式
        log.debug("建議線程池大小調整為: {} (基于QPS: {})", suggestedSize, qps);
    }
}

五、總結:給系統(tǒng)做QPS監(jiān)控就像...

1.為什么要監(jiān)控QPS?

  • 對系統(tǒng):就像給汽車裝時速表,超速了會報警
  • 對開發(fā):就像給程序員裝"健康手環(huán)",代碼跑太快會冒煙
  • 對老板:就像給公司裝"業(yè)績大屏",數字好看心情好

2.各方案選擇建議:

  • 初創(chuàng)公司/小項目:用方案1,簡單粗暴見效快,就像"創(chuàng)可貼"
  • 中型項目/微服務:用方案2,全面體檢不遺漏,就像"年度體檢"
  • 大型分布式系統(tǒng):用方案3,全景監(jiān)控無死角,就像"衛(wèi)星監(jiān)控"

3.最佳實踐提醒:

// 記住這些黃金法則:
public class QpsGoldenRules {
    // 法則1:監(jiān)控不是為了監(jiān)控而監(jiān)控
    public static final String RULE_1 = "別讓監(jiān)控把系統(tǒng)壓垮了!";
    
    // 法則2:告警要有意義
    public static final String RULE_2 = "狼來了喊多了,就沒人信了!";
    
    // 法則3:數據要可視化
    public static final String RULE_3 = "老板看不懂的圖表都是廢紙!";
    
    // 法則4:要有應對方案
    public static final String RULE_4 = "光報警不解決,要你有何用?";
}

4.最后總結:

給你的系統(tǒng)加QPS監(jiān)控,就像是:

  • 給外賣小哥配了計步器 —— 知道他每天跑多少
  • 給程序員裝了鍵盤計數器 —— 知道他有多卷
  • 給系統(tǒng)裝了"心電圖機" —— 隨時掌握生命體征

記住,一個健康的系統(tǒng)應該:

  • 平時 心跳平穩(wěn)(QPS穩(wěn)定)
  • 大促時 適當興奮(彈性擴容)
  • 故障時 自動降壓(熔斷降級)

現在就去給你的SpringBoot系統(tǒng)裝上"智能手環(huán)"吧!讓它在代碼的海洋里,游得更快、更穩(wěn)、更健康!

最后的最后:監(jiān)控千萬條,穩(wěn)定第一條;QPS不規(guī)范,運維兩行淚!

以上就是SpringBoot實現QPS監(jiān)控的完整代碼的詳細內容,更多關于SpringBoot實現QPS監(jiān)控的資料請關注腳本之家其它相關文章!

相關文章

最新評論

永丰县| 新和县| 梁河县| 洞口县| 汤阴县| 拉萨市| 泰兴市| 上栗县| 绍兴市| 久治县| 盈江县| 永昌县| 朝阳区| 新沂市| 琼海市| 威海市| 林甸县| 崇义县| 犍为县| 正安县| 崇左市| 梨树县| 曲周县| 永川市| 莎车县| 沁阳市| 巴林右旗| 定州市| 忻城县| 锦州市| 神木县| 安多县| 巴彦县| 康定县| 化德县| 安阳市| 贵州省| 平顺县| 柘荣县| 普格县| 吉木萨尔县|