SpringBoot 实现QPS监控:别等系统“咳血”了才想起装“心电图”!

简介: 想象一下你的系统就像一个忙碌的外卖小哥,QPS就是他每秒能送多少份外卖!如果小哥每秒只能送1单,那估计顾客早就饿晕在厕所了;要是每秒能送100单,那他绝对是"闪电侠"附体!

大家好,我是小悟。

一、什么是QPS?—— 系统的”心跳频率” 💗

想象一下你的系统就像一个忙碌的外卖小哥,QPS(Query Per Second)就是他每秒能送多少份外卖!如果小哥每秒只能送1单,那估计顾客早就饿晕在厕所了;要是每秒能送100单,那他绝对是”闪电侠”附体!

正常系统的QPS就像人的心跳:

  • 60-100 QPS:健康小伙子,心跳平稳
  • 100-1000 QPS:健身达人,有点小激动
  • 1000+ QPS:跑马拉松呢!快喘口气!
  • 10000+ QPS:这货是打了鸡血吧?

二、方案大比拼——给系统装上”智能手环”

方案1:简易版手环(AOP拦截器

适合小项目,就像给系统戴个手环

@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监控龟龟已启动,开始慢慢爬...");
        // 每秒统计一次,像乌龟一样稳定
        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;
            // 顺便记录一下响应时间,看看系统是不是"老了腿脚慢"
            if (cost > 1000) {
                log.warn("方法 {} 执行了 {}ms,比蜗牛还慢!", methodName, cost);
            }
        }
    }
    
    private void printQps() {
        if (counterMap.isEmpty()) {
            log.info("系统在睡大觉,没有请求...");
            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:专业版体检仪(Micrometer + Prometheus)

适合大项目,就像给系统做全面体检

@Configuration
public class MetricsConfig {
    
    @Bean
    public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
        return registry -> {
            registry.config().commonTags("application", "my-awesome-app");
            log.info("系统体检中心开业啦!欢迎随时来检查身体~");
        };
    }
}
@Service
public class OrderService {
    
    private final Counter orderCounter;
    private final Timer orderTimer;
    private final MeterRegistry meterRegistry;
    
    public OrderService(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
        
        // 创建订单计数器,像收银机一样"叮叮叮"
        this.orderCounter = Counter.builder("order.count")
                .description("订单数量统计")
                .tag("type", "create")
                .register(meterRegistry);
        
        // 创建订单耗时计时器
        this.orderTimer = Timer.builder("order.process.time")
                .description("订单处理时间")
                .register(meterRegistry);
    }
    
    public Order createOrder(OrderDTO dto) {
        // 记录方法执行时间
        return orderTimer.record(() -> {
            log.debug("正在打包订单,请稍候...");
            
            // 业务逻辑...
            Order order = doCreateOrder(dto);
            
            // 订单创建成功,计数器+1
            orderCounter.increment();
            
            // 动态QPS统计(最近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;
        });
    }
    
    // 动态查看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", "系统当前状态良好,吃嘛嘛香!");
        
        return metrics;
    }
}

方案3:豪华版监控大屏(Spring Boot Admin

给老板看的,必须高大上!

# application.yml
spring:
  boot:
    admin:
      client:
        url: http://localhost:9090  # Admin Server地址
        instance:
          name: "青龙系统"
          metadata:
            owner: "码农小张"
            department: "爆肝事业部"
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("║        系统QPS监控大屏                     ║\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");
        
        // 添加系统健康建议
        dashboard.append("\n 系统建议:\n");
        double maxQps = serviceQps.values().stream().max(Double::compare).orElse(0.0);
        if (maxQps < 50) {
            dashboard.append("   系统有点闲,可以考虑接点私活~ \n");
        } else if (maxQps > 1000) {
            dashboard.append("   系统快冒烟了!快加机器!\n");
        } else {
            dashboard.append("   状态完美,继续保持!\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);
                // 这里可以接入钉钉/企业微信告警
                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("已发送钉钉告警:{}", message);
    }
}

三、方案详细实施步骤

方案1实施步骤(简易版):

  1. 添加依赖:给你的pom.xml来点”维生素”
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 启用AOP:在主类上贴个”创可贴”
@SpringBootApplication
@EnableAspectJAutoProxy  // 启用AOP魔法
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
        System.out.println("QPS监控小鸡破壳而出!");
    }
}
  1. 创建切面类:如上文的QpsMonitorAspect
  2. 测试一下:疯狂刷新接口,看看控制台输出

方案2实施步骤(专业版):

  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. 访问监控数据
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("监控大屏已就位,陛下请检阅!");
    }
}
  1. 客户端配置:如上文yml配置
  2. 访问Admin UIhttp://localhost:9090

四、QPS统计的进阶技巧

1. 滑动窗口统计(最近N秒的QPS)

public class SlidingWindowQpsCounter {
    // 用环形队列实现滑动窗口
    private final LinkedList<Long> timestamps = new LinkedList<>();
    private final int windowSeconds;
    
    public SlidingWindowQpsCounter(int windowSeconds) {
        this.windowSeconds = windowSeconds;
        log.info("创建滑动窗口监控,窗口大小:{}秒", 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. 分位数统计(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("分位数统计已启用,准备精准打击慢查询!");
    };
}

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("熔断器关闭,系统恢复供电!当前QPS: {}", currentQps);
        } else if (!circuitOpen && currentQps > 1000) {
            circuitOpen = true;
            log.error("熔断器触发!QPS过高: {},系统进入保护模式", currentQps);
        }
        
        // 动态调整线程池大小
        adjustThreadPool(currentQps);
    }
    
    private void adjustThreadPool(double qps) {
        int suggestedSize = (int) (qps * 0.5);  // 经验公式
        log.debug("建议线程池大小调整为: {} (基于QPS: {})", suggestedSize, qps);
    }
}

五、总结:给系统做QPS监控就像…

1. 为什么要监控QPS?

  • 对系统:就像给汽车装时速表,超速了会报警
  • 对开发:就像给程序员装”健康手环”,代码跑太快会冒烟
  • 对老板:就像给公司装”业绩大屏”,数字好看心情好

2. 各方案选择建议

  • 初创公司/小项目:用方案1,简单粗暴见效快,就像”创可贴”
  • 中型项目/微服务:用方案2,全面体检不遗漏,就像”年度体检”
  • 大型分布式系统:用方案3,全景监控无死角,就像”卫星监控”

3. 最佳实践提醒

// 记住这些黄金法则:
public class QpsGoldenRules {
    // 法则1:监控不是为了监控而监控
    public static final String RULE_1 = "别让监控把系统压垮了!";
    
    // 法则2:告警要有意义
    public static final String RULE_2 = "狼来了喊多了,就没人信了!";
    
    // 法则3:数据要可视化
    public static final String RULE_3 = "老板看不懂的图表都是废纸!";
    
    // 法则4:要有应对方案
    public static final String RULE_4 = "光报警不解决,要你有何用?";
}

4. 最后总结

给你的系统加QPS监控,就像是:

  • 给外卖小哥配了计步器 —— 知道他每天跑多少
  • 给程序员装了键盘计数器 —— 知道他有多卷
  • 给系统装了”心电图机” —— 随时掌握生命体征

记住,一个健康的系统应该:

  • 平时 心跳平稳(QPS稳定)
  • 大促时 适当兴奋(弹性扩容)
  • 故障时 自动降压(熔断降级)

现在就去给你的SpringBoot系统装上”智能手环”吧!让它在代码的海洋里,游得更快、更稳、更健康!

最后的最后:监控千万条,稳定第一条;QPS不规范,运维两行泪!

SpringBoot 实现QPS监控:别等系统“咳血”了才想起装“心电图”!.png

谢谢你看我的文章,既然看到这里了,如果觉得不错,随手点个赞、转发、在看三连吧,感谢感谢。那我们,下次再见。


您的一键三连,是我更新的最大动力,谢谢

山水有相逢,来日皆可期,谢谢阅读,我们再会

我手中的金箍棒,上能通天,下能探海

相关文章
|
23天前
|
人工智能 自然语言处理 安全
从工具到“24/7员工”!OpenClaw阿里云/本地部署+API配置+股票分析等15个实用Skill实战及避坑指南
OpenClaw的生态正以爆发式速度成长,其能力边界完全取决于所装备的Skills(技能插件)。作为可7×24小时待命的“AI员工”,仅靠基础功能远不足以发挥其价值——真正的高效能,来自于针对性的Skill组合。本文精选15个覆盖生产力、语音通信、数据分析、开发工具、生活娱乐的核心Skill,从自我迭代到股价分析,从离线音频转写到智能家居控制,全方位拓展OpenClaw能力,同时完整呈现2026年OpenClaw零基础阿里云及本地部署流程、阿里云百炼API配置要点,所有代码命令可直接复制执行,无营销词汇,助力新手快速打造专属“全能AI助手”。
1131 11
|
23天前
|
人工智能 测试技术 微服务
AI 大型项目编程流程
本项目采用Claude与Codex协同开发模式:先由Claude定稿需求、竞品分析、生成技术文档;再由Codex分周期开发、自动生成/更新流程文档,并循环接受Claude评估优化;老项目则支持微服务级模块化改造与迭代测试,实现高效、可靠、可追溯的AI驱动开发闭环。(239字)
230 7
|
16天前
|
JSON Java Maven
【Maven】pom.xml 超全面详解(核心结构+所有常用依赖)
本文详解Maven核心配置文件pom.xml的七大结构(坐标、属性、依赖、构建等),并分类梳理Web、数据库、工具、测试、日志、JSON等场景下最常用依赖的功能与用法,助开发者高效管理项目依赖与构建流程。
|
23天前
|
存储 人工智能 Shell
【从零手写 ClaudeCode:learn-claude-code 项目实战笔记】(3)TodoWrite (待办写入)
本章详解 s03 版本 TodoWrite 机制:通过 `todo` 工具+`TodoManager` 实现显式任务状态管理(pending/in_progress/completed),强制单任务聚焦;并引入“nag 提醒”——连续3轮未更新待办时自动注入提醒,解决大模型长链路任务健忘问题。代码精简可运行。
332 3
|
18天前
|
存储 人工智能 关系型数据库
OpenClaw怎么可能没痛点?用RDS插件来释放OpenClaw全部潜力
OpenClaw插件是深度介入Agent生命周期的扩展机制,提供24个钩子,支持自动注入知识、持久化记忆等被动式干预。相比Skill/Tool,插件可主动在关键节点(如对话开始/结束)执行逻辑,适用于RAG增强、云化记忆等高级场景。
736 56
OpenClaw怎么可能没痛点?用RDS插件来释放OpenClaw全部潜力
|
23天前
|
运维 关系型数据库 MySQL
告别SQL指令!OpenClaw(Clawdbot)阿里云部署集成MySQL专属Skill +免费API配置及避坑手册
在数据库运维场景中,复杂的SQL指令、频繁的状态巡检、突发的故障排查,往往占用技术人员大量时间。而OpenClaw(原Clawdbot)作为2026年爆火的开源AI助手框架,与火山引擎云数据库MySQL版的结合,彻底改变了这一现状——通过配置`volcengine-rds-mysql`专属Skill,即可用自然语言实现数据库实例管理、数据查询、性能监控、故障排查,甚至7×24小时智能管控,大幅降低运维门槛与成本。
534 2
|
23天前
|
人工智能 安全 JavaScript
保姆级教学:OpenClaw阿里云/本地部署+5个必装Skill+百炼API配置及常见问题排查
OpenClaw的本质是一套开源AI框架,本身仅具备基础对话能力,真正让它实现“从能聊到能干”的核心,是其丰富的Skills生态——官方技能市场收录5705个技能,覆盖办公、调研、安全、知识管理等全场景。但多数用户会陷入“技能堆砌”陷阱,盲目安装大量工具后,不仅没提升效率,反而因恶意代码、功能冲突、权限泄露等问题踩坑。
789 9
|
23天前
|
安全 API 决策智能
AutoGen 架构演进全梳理:从 v0.4 到 Microsoft Agent Framework
Microsoft AutoGen(2023–2025)是开创性多智能体开源框架,以“群聊式协作”替代单Prompt链,支持智能体对话、工具调用、代码执行与人工介入。v0.4达技术巅峰,后并入Microsoft Agent Framework(MAF)。其核心范式——LLM专家团队协同——已成行业标准。
685 3
AutoGen 架构演进全梳理:从 v0.4 到 Microsoft Agent Framework
|
23天前
|
人工智能 安全 API
保姆级图文教程!OpenClaw阿里云/本地MacOS/Windows部署+免费大模型API配置接入15000+Skill指南
当ClawHub的技能数量突破14946个,OpenClaw早已从“单一AI工具”进化为“模块化生产力生态”。技能(Skill)作为AI的“执行身份”,让OpenClaw能在设计总监、增长黑客、法律顾问等角色间无缝切换,一个人+一套技能组合即可搭建微型工作室。但繁华背后暗藏危机——2026年初爆发的ClawHavoc事件,335个恶意技能通过远程代码执行漏洞窃取API Key、注入恶意脚本,让无数用户遭遇“挖矿盗刷”的损失。
1316 16
|
19天前
|
人工智能 JavaScript Linux
【最新版养 AI龙虾🦞指南】零基础 OpenClaw 阿里云/本地部署、配置、使用保姆级教程
OpenClaw(原Clawdbot,曾用名Moltbot)作为一款开源轻量级AI自动化代理工具,2026年版本在部署灵活性、功能兼容性上实现重大升级,核心优势在于“自然语言驱动+全流程任务自动化”,无需手动编写脚本,仅需输入口语化指令,即可完成文档处理、日程管理、文件读写、跨工具协同、代码生成等各类重复性工作,被广泛应用于个人办公、新手开发、轻量团队协作等场景,堪称“私人AI员工”。
992 92

热门文章

最新文章