Avoid QPS Miscalculations: 5 Proven Methods to Accurately Measure Traffic
This article explains five practical ways to count QPS—from gateway and application instrumentation to monitoring tools, log analysis, and database metrics—while highlighting common pitfalls such as health‑check filtering, thread‑safety, and multi‑node aggregation, helping engineers make informed scaling decisions.
1. Clarify the granularity of QPS for different business scenarios
Before choosing a method, determine what granularity you need: e.g., per‑interface for flash‑sale, per‑service for micro‑service clusters, per‑method for API performance, or daily/peak aggregation for offline capacity planning.
2. Five common QPS counting methods
Method 1 – Gateway‑level counting (global view, suitable for distributed projects)
Applicable scenario : micro‑service clusters that need total QPS or entry QPS from an API gateway or Nginx.
Principle : intercept every request at the gateway, record the count and timestamp, and compute QPS per second.
Practical example 1: Nginx QPS statistics (recommended for small‑to‑medium projects)
Enable stub_status in nginx.conf and record detailed logs for offline analysis.
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;
}
}
}Query http://192.168.0.100:8080/nginx-status to see active connections and request counts, then calculate QPS as requests / time.
Shell script to poll the status page every second:
while true; do
current=$(curl -s http://192.168.0.100:8080/nginx-status | awk 'NR==3 {print $3}')
sleep 1
next=$(curl -s http://192.168.0.100:8080/nginx-status | awk 'NR==3 {print $3}')
qps=$((next - current))
echo "Current QPS: $qps"
donePitfalls : health‑check requests (e.g., /actuator/health) are counted by default and must be filtered.
Practical example 2: Spring Cloud Gateway QPS statistics (Java micro‑services)
Implement a GlobalFilter that stores a AtomicLong per path, prints the QPS each second, and skips health‑check paths.
@Component
public class QpsStatisticsFilter implements GlobalFilter, Ordered {
private final Map<String, AtomicLong> pathQpsMap = new ConcurrentHashMap<>();
@PostConstruct
public void init() {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(() -> {
pathQpsMap.forEach((path, counter) -> {
long qps = counter.getAndSet(0);
log.info("Interface[{}] QPS: {}", path, qps);
});
}, 0, 1, TimeUnit.SECONDS);
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String path = exchange.getRequest().getPath().value();
if (path.startsWith("/actuator")) return chain.filter(exchange);
pathQpsMap.computeIfAbsent(path, k -> new AtomicLong()).incrementAndGet();
return chain.filter(exchange);
}
@Override
public int getOrder() { return -1; }
}Method 2 – Application‑level instrumentation (fine‑grained, suitable for single‑service interface statistics)
Use AOP or servlet filters to intercept controller methods, count requests with AtomicLong, and print QPS every second.
Practical example: Spring AOP QPS statistics
@Aspect
@Component
@Slf4j
public class ApiQpsAspect {
private final Map<String, AtomicLong> apiQpsMap = new ConcurrentHashMap<>();
@PostConstruct
public void scheduleQpsPrint() {
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
apiQpsMap.forEach((api, counter) -> {
long qps = counter.getAndSet(0);
if (qps > 0) log.info("[QPS] API: {}, QPS: {}", api, qps);
});
}, 0, 1, TimeUnit.SECONDS);
}
@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();
}
}Filter out non‑200/300 responses and optionally record response time to compute average latency.
Method 3 – Monitoring tools (real‑time visualization, suitable for operations)
Expose metrics via Micrometer, scrape them with Prometheus, and visualise in Grafana.
Practical example: Spring Boot + Prometheus + Grafana
Add micrometer-registry-prometheus and spring-boot-starter-actuator dependencies, enable the /actuator/prometheus endpoint, and register a counter for the /order/create API.
@RestController
@RequestMapping("/order")
public class OrderController {
private final MeterRegistry meterRegistry;
@Autowired
public OrderController(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@PostMapping("/create")
public String createOrder() {
Counter.builder("order.create.qps")
.description("Order creation QPS")
.register(meterRegistry)
.increment();
return "success";
}
}Configure prometheus.yml to scrape the service every second and create a Grafana panel with the query sum(rate(order_create_qps_total[1m])) by (application). Set alerts for thresholds.
Method 4 – Log‑analysis statistics (offline, suitable for capacity evaluation)
Write structured JSON logs (e.g., with Logback), collect them with Logstash into Elasticsearch, and visualise QPS in Kibana.
Practical example: ELK QPS analysis
Logback configuration writes request path and timestamp to a JSON file. A Logstash pipeline parses the JSON, converts the timestamp, and indexes into order-request-*. Kibana’s “Discover” and “Visualize” can then plot request count per second.
Method 5 – Database‑level auxiliary statistics (indirect, useful for DB bottleneck diagnosis)
Monitor MySQL connection count and queries‑per‑second, or enable slow‑query logging, to infer application QPS.
show status like 'Threads_connected';
show status like 'Queries';Configure slow_query_log in my.cnf to capture long‑running SQL.
Selection guide and pitfall checklist
Choose the method that matches your scenario: global real‑time monitoring → gateway + Prometheus; single‑service fine‑grained → AOP + Micrometer; offline capacity planning → ELK; DB bottleneck → DB metrics. Common pitfalls include counting health‑check requests, ignoring thread‑safety, setting too aggressive Prometheus scrape intervals, and forgetting to aggregate data across multiple nodes.
Conclusion
QPS statistics serve decision‑making: determine whether the system can handle traffic and when to scale. Accurate counting is less important than filtering invalid requests, ensuring concurrency safety, and aligning metrics with business context.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
