Nginx与Spring Cloud Gateway QPS统计全攻略

一、为什么QPS统计如此重要

1. 性能监控的核心指标

  • QPS(Queries Per Second):每秒查询率,是衡量系统性能的关键指标
  • 容量规划:帮助预估系统承载能力和扩展需求
  • 故障预警:及时发现系统性能瓶颈和异常

2. 不同场景下的QPS统计策略

  • 网关层统计:适用于微服务架构的整体监控
  • 应用层统计:适用于具体业务接口的精细化监控
  • 基础设施统计:基于Nginx等基础组件的统计方案

二、Nginx网关层QPS统计实战

1. 基础配置实现

1.1 nginx.conf配置详解
ini 复制代码
http {
    # 开启状态监控页面
    server {
        listen 8080;
        location /nginx-status {
            stub_status on;
            allow 192.168.0.0/24; # 只允许内网访问
            deny all;
        }
    }

    # 记录详细请求日志(用于离线分析)
    log_format main '$remote_addr [$time_local] "$request" $status $request_time';
    server {
        listen 80;
        server_name api.example.com;
        access_log /var/log/nginx/api-access.log main; # 日志路径

        # 转发到后端服务
        location / {
            proxy_pass http://backend-service;
        }
    }
}

配置要点说明:

  • stub_status on:启用Nginx状态模块
  • 访问控制:只允许内网IP访问状态页面,保证安全性
  • 自定义日志格式:便于后续分析
1.2 实时QPS查看

访问 http://192.168.0.100:8080/nginx-status,返回信息:

yaml 复制代码
Active connections: 200
server accepts handled requests
 10000  10000  100000
Reading: 0 Writing: 1  Waiting: 190

数据解读:

  • Active connections:当前活跃连接数
  • accepts/handled/requests:分别表示接受的连接数、处理的连接数、总请求数
  • QPS计算公式:requests/时间间隔 100000/10 =10000 QPS

2. 自动化QPS统计脚本

2.1 Shell脚本实现
bash 复制代码
while true; do
    # 取当前请求数
    current=$(curl -s http://192.168.0.100:8080/nginx-status | awk 'NR==3 {print $3}')
    sleep 1
    # 取1秒后请求数
    next=$(curl -s http://192.168.0.100:8080/nginx-status | awk 'NR==3 {print $3}')
    qps=$((next - current))
    echo "当前QPS: $qps"
done
2.2 脚本优化建议
  • 添加错误处理机制
  • 输出格式化为JSON便于集成
  • 支持阈值告警功能

三、Spring Cloud Gateway QPS统计实践

1. 自定义全局过滤器实现

1.1 QpsStatisticsFilter核心代码
java 复制代码
@Component
public class QpsStatisticsFilter implements GlobalFilter, Ordered {
    // 存储接口QPS:key=接口路径,value=原子计数器
    private final Map<String, AtomicLong> pathQpsMap = new ConcurrentHashMap<>();

    // 定时1秒清零计数器(避免数值过大)
    @PostConstruct
    public void init() {
        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        executor.scheduleAtFixedRate(() -> {
            // 遍历所有接口,打印QPS后清零
            pathQpsMap.forEach((path, counter) -> {
                long qps = counter.getAndSet(0);
                log.info("接口[{}] QPS: {}", path, qps);
            });
        }, 0, 1, TimeUnit.SECONDS);
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取请求路径(如/order/seckill)
        String path = exchange.getRequest().getPath().value();
        // 计数器自增(线程安全)
        pathQpsMap.computeIfAbsent(path, k -> new AtomicLong()).incrementAndGet();
        // 继续转发请求
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -1; // 过滤器优先级:数字越小越先执行
    }
}
1.2 关键设计思路
  • ConcurrentHashMap:保证线程安全的同时提供高并发性能
  • AtomicLong:原子操作确保计数准确性
  • ScheduledExecutorService:定时任务清理计数器

2. 踩坑经验总结

2.1 健康检查请求过滤
java 复制代码
if (path.startsWith("/actuator")) return chain.filter(exchange);

四、应用层AOP QPS统计深度解析

1. Spring AOP实现方案

1.1 依赖配置
xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
1.2 自定义切面实现
java 复制代码
@Aspect
@Component
@Slf4j
public class ApiQpsAspect {
    // 存储接口QPS:key=接口名(如com.example.OrderController.createOrder),value=计数器
    private final Map<String, AtomicLong> apiQpsMap = new ConcurrentHashMap<>();

    // 定时1秒打印QPS并清零
    @PostConstruct
    public void scheduleQpsPrint() {
        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
            apiQpsMap.forEach((api, counter) -> {
                long qps = counter.getAndSet(0);
                if (qps > 0) { // 只打印有请求的接口
                    log.info("[QPS统计] 接口: {}, QPS: {}", api, qps);
                }
            });
        }, 0, 1, TimeUnit.SECONDS);
    }

    // 切入点:拦截所有Controller方法
    @Pointcut("execution(* com.example.*.controller..*(..))")
    public void apiPointcut() {}

    // 环绕通知:统计请求数
    @Around("apiPointcut()")
    public Object countQps(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取接口名(类名+方法名)
        String apiName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
        // 计数器自增
        apiQpsMap.computeIfAbsent(apiName, k -> new AtomicLong()).incrementAndGet();
        // 执行原方法
        return joinPoint.proceed();
    }
}

2. 进阶优化方案

2.1 有效请求过滤
java 复制代码
// 在countQps方法中添加响应状态判断
if (response.getStatusCode().is2xxSuccessful()) {
    apiQpsMap.computeIfAbsent(apiName, k -> new AtomicLong()).incrementAndGet();
}
2.2 响应时间统计增强
java 复制代码
// 记录响应时间
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long cost = System.currentTimeMillis() - start;
// 存储响应时间(key=接口名,value=时间列表)
timeMap.computeIfAbsent(apiName, k -> new CopyOnWriteArrayList<>()).add(cost);
// 计算平均响应时间
double avgTime = timeMap.get(apiName).stream().mapToLong(Long::longValue).average().orElse(0);

3. 生产环境最佳实践

3.1 并发安全保障
  • 必须使用AtomicLong进行计数
  • 避免long变量的线程安全问题
3.2 性能影响控制
  • AOP开销:单请求约0.1ms,影响较小
  • 条件启用 :使用@Conditional控制只在非生产环境启用
  • Java Agent替代:减少代码侵入性

五、三种方案对比与选型建议

方案 优势 劣势 适用场景
Nginx统计 简单易用,性能好 无法区分业务接口 中小项目,基础监控
Gateway统计 统一入口,功能丰富 部署复杂,学习成本高 微服务架构
AOP统计 精细控制,业务感知强 侵入性强,性能开销 单体应用,业务监控

选型建议:

  1. 中小项目:优先选择Nginx方案
  2. 微服务架构:推荐Gateway方案
  3. 精细化监控:采用AOP方案
  4. 混合使用:多维度监控,相互验证

通过本文的深入分析,相信你已经掌握了不同场景下QPS统计的最佳实践。记住,选择合适的方案比盲目追求复杂的技术更重要!

相关推荐
侠客行03173 小时前
Mybatis连接池实现及池化模式
java·mybatis·源码阅读
蛇皮划水怪3 小时前
深入浅出LangChain4J
java·langchain·llm
Victor3563 小时前
https://editor.csdn.net/md/?articleId=139321571&spm=1011.2415.3001.9698
后端
Victor3563 小时前
Hibernate(89)如何在压力测试中使用Hibernate?
后端
灰子学技术4 小时前
go response.Body.close()导致连接异常处理
开发语言·后端·golang
老毛肚4 小时前
MyBatis体系结构与工作原理 上篇
java·mybatis
风流倜傥唐伯虎5 小时前
Spring Boot Jar包生产级启停脚本
java·运维·spring boot
不像程序员的程序媛5 小时前
Nginx日志切分
服务器·前端·nginx
Yvonne爱编码5 小时前
JAVA数据结构 DAY6-栈和队列
java·开发语言·数据结构·python
Re.不晚5 小时前
JAVA进阶之路——无奖问答挑战1
java·开发语言