nginx限流
Nginx 限流测试完全指南
一、快速开始
1. Nginx 配置(http 块)
http
{
set_real_ip_from 100.127.0.0/16; # 腾讯云 CLB 实际使用的网段
set_real_ip_from 10.206.0.0/24; # 保留原有的(如果有用)
real_ip_header X-Forwarded-For;
real_ip_recursive on;
# 全局限流:4000 req/s
limit_req_zone $server_name
zone=global:10m
rate=80r/s;
# 限流区域定义
limit_req_zone $binary_remote_addr zone=global_limit:10m rate=3r/s;# 添加全局限流
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
2. Nginx 配置(server 块)
server
{
#PHP-INFO-START PHP引用配置,可以注释或修改
# include enable-php-81.conf;
location ~ [^/]\.php(/|$){
limit_req zone=global burst=30 nodelay;
# limit_req zone=global_limit burst=2 nodelay;
error_page 503 = @limit_json;
try_files $uri =404;
fastcgi_pass unix:/tmp/php-cgi-81.sock;
fastcgi_index index.php;
include fastcgi.conf;
include pathinfo.conf;
}
location / {
# 先过全局限流(总闸门)
limit_req zone=global burst=30 nodelay;
# 再过单IP限流(防单点)
# limit_req zone=global_limit burst=2 nodelay;
error_page 503 = @limit_json;
if (!-e $request_filename){
rewrite ^(.*)$ /index.php?s=$1 last; break;
}
}
location @limit_json {
default_type application/json;
return 200 '{"code":429,"msg":"前方拥挤,请稍后重试"}';
}
3. k6 测试脚本
import http from 'k6/http';
import { Counter } from 'k6/metrics';
const limitedRequests = new Counter('limited_requests');
const normalRequests = new Counter('normal_requests');
export let options = {
// 方案 1:固定高并发(同步,会被响应时间阻塞)
// vus: 50,
// duration: '10s',
// 方案 2:固定请求速率(不受响应时间影响)
scenarios: {
constant_request_rate: {
executor: 'constant-arrival-rate',
rate: 3000, // 每秒发送 1500 个请求 ⚠️如果这里的vu数不够 很难测试准确限流数
timeUnit: '1s', // 时间单位
duration: '10s', // 持续 10 秒
preAllocatedVUs: 100, // 预分配 100 个 VU
maxVUs: 500, // 最多 500 个 VU(应对慢响应) ⚠️如果这里的vu数不够 很难测试准确限流数
},
},
// 方案 3:阶梯式压测(注释掉上面的,启用下面的)
// stages: [
// { duration: '5s', target: 100 },
// { duration: '10s', target: 100 },
// { duration: '5s', target: 0 },
// ],
};
export default function () {
// 单台服务器测试
let res = http.get('http://domain:8001/index/index/index');
// CLB 负载均衡测试
// let res = http.get('http://domain');
// 判断是否被限流
let isLimited = false;
try {
const body = JSON.parse(res.body);
if (body.code === 429 || body.code === '429') {
isLimited = true;
limitedRequests.add(1);
// 打印限流响应
console.log(`🚫 [限流] 状态码: ${res.status} | 响应: ${res.body}`);
} else {
normalRequests.add(1);
// 打印正常响应(只显示前100字符)
console.log(`✅ [通过] 状态码: ${res.status} | 响应: ${res.body.substring(0, 100)}...`);
}
} catch (e) {
normalRequests.add(1);
// 打印HTML响应(只显示前100字符)
console.log(`✅ [通过] 状态码: ${res.status} | HTML响应: ${res.body.substring(0, 100)}...`);
}
// 不要 sleep,让请求尽可能快
}
export function handleSummary(data) {
const totalReqs = data.metrics.http_reqs.values.count;
const rate = data.metrics.http_reqs.values.rate;
const limited = data.metrics.limited_requests ? data.metrics.limited_requests.values.count : 0;
const normal = data.metrics.normal_requests ? data.metrics.normal_requests.values.count : 0;
console.log('\n========== 限流测试结果 ==========');
console.log(`总请求数: ${totalReqs}`);
console.log(`实际速率: ${rate.toFixed(2)} req/s`);
console.log(`正常通过: ${normal} 次 (${(normal/totalReqs*100).toFixed(1)}%)`);
console.log(`被限流: ${limited} 次 (${(limited/totalReqs*100).toFixed(1)}%)`);
console.log('');
if (rate < 40) {
console.log('⚠️ 实际速率低于预期,可能是:');
console.log(' 1. 网络延迟较高');
console.log(' 2. 服务器响应慢');
console.log(' 3. 并发数不够');
} else if (limited === 0) {
console.log('❌ 限流未生效!');
} else if (limited / totalReqs > 0.5) {
console.log('✅ 限流正常工作!');
} else {
console.log('⚠️ 限流部分生效');
}
// return {
// 'stdout': JSON.stringify(data, null, 2),
// };
}
4. 运行测试
k6 run high_concurrency_test.js
二、核心概念
关键参数
| 参数 | 说明 | 示例 |
|---|---|---|
| rate | 每秒允许的请求数 | 80r/s = 每秒 80 个请求 |
| burst | 突发流量缓冲区 | 30 = 可额外处理 30 个请求 |
| nodelay | 立即处理 | 不排队,立即返回 |
Token Bucket 算法
核心公式:
总通过数 = rate × 时间 + burst
示例:
rate=80r/s, burst=30, 测试 10 秒
理论通过 = 80 × 10 + 30 = 830 个
三、参数配置
k6 配置建议
k6 rate = Nginx 限流 × 25-50 倍
示例:
Nginx 限流 = 80 req/s
k6 rate = 2000-4000 req/s
maxVUs = rate × 响应时间
示例:
rate = 3000, 响应时间 = 0.2s
maxVUs = 3000 × 0.2 = 600
四、常见问题(大白话)
Q1: 为什么 VU 要等待?
银行例子:
员工去银行办事:
1. 去银行(发送请求)
2. 等待办理(等待响应)⏳ 必须等!
3. 办完了(收到响应)
4. 才能再去下一次
办事快(10ms):1 个员工 10 秒能去 1000 次 ✅
办事慢(747ms):1 个员工 10 秒只能去 13 次 ❌
关键:
- Nginx 很快(< 1ms)
- 但要等整个链路(包括 PHP)
- 慢的是 PHP,不是 Nginx
实际数据:
你的测试(固定 50 VU):
响应时间:747ms
每个 VU:10,000ms ÷ 747ms = 13.4 次
总请求:13.4 × 50 = 670 次
问题:VU 被阻塞,发送请求太少
Q2: 提高 rate 和 maxVUs,通过数会增加吗?
答案:不会!
银行规定:每分钟办理 80 人(Nginx 限流)
你派 200 个员工:银行接待 830 人
你派 500 个员工:银行接待 830 人
你派 1000 个员工:银行接待 830 人
无论派多少员工,银行只接待 830 人!
数据验证:
| rate | maxVUs | 总请求 | 通过 | 通过率 |
|---|---|---|---|---|
| 1500 | 200 | 3,163 | 750 | 23.7% |
| 1500 | 500 | 14,674 | 837 | 5.7% |
| 3000 | 500 | 29,515 | 829 | 2.8% |
结论:
- 总请求增加 ✅
- 通过数不变(~830)✅
- 通过率下降 ✅
Q3: 为什么第一次只通过 750 个?
问题:
配置:rate=80r/s, burst=30
理论通过:830 个
实际通过:750 个
差距:80 个(9.6%)
原因(银行例子):
你派了 200 个员工,办事慢(588ms)
第 1 分钟:
- 110 人进去了(80+30)
- 这 110 人在办事(需要 35 秒)
- 剩余员工:200 - 110 = 90 人
第 2 分钟:
- 80 人进去了
- 剩余员工:90 - 80 = 10 人
- ⚠️ 前面 110 人还在办事中
第 3 分钟:
- 10 人进去了
- 剩余员工:10 - 10 = 0 人
- ❌ 所有 200 个员工都在银行里等着
- ❌ 没有空闲员工继续排队了!
第 4-10 分钟:
- 没有员工可以去排队
- 虽然银行还能接待人
- 但你没人去了!
结果:
- 只办理了 750 人
- 不是银行的问题
- 是你的员工不够用!
实际数据:
测试 1(maxVUs=200):
总请求:3,163 个
正常通过:750 个
实际速率:157 req/s
VUs 使用:max=200(用满了)❌
dropped_iterations: 1838(丢弃了很多)
测试 2(maxVUs=500):
总请求:14,674 个
正常通过:837 个
实际速率:983 req/s
VUs 使用:max=426(没用满)✅
dropped_iterations: 327(少很多)
结论:
- maxVUs 不够 → VU 用满 → 测试不完整 ❌
- maxVUs 够用 → 测试正常 → 结果准确 ✅
五、测试结果
完美的测试
配置:rate=80r/s, burst=30
k6:rate=3000, maxVUs=500
结果:
总请求:29,515 个
通过:829 个
被限流:28,686 个
验证:
理论:830 个
实际:829 个
误差:0.12% 🎯
六、银行例子(大白话)
银行规定
银行:每分钟办理 80 人,排队区 30 人
10 分钟总共接待:830 人
这是银行规定,不会变!
你的测试演进
测试 1:派 50 个员工(固定 VU)
办事慢(747ms)
每个员工 10 秒去 13 次
50 个员工去了 650 次
银行接待 627 人(91.8%)
问题:
- 员工太少
- 被响应时间阻塞
- 银行没忙起来 ❌
测试 2:派 200 个员工(maxVUs 不够)
办事慢(588ms)
前 3 分钟:200 个员工用完了
后 7 分钟:没有空闲员工了
员工去了 3,163 次
银行接待 750 人(23.7%)
问题:
- 正在办理的占用了员工
- 没有空闲员工继续排队
- 测试不完整 ❌
测试 3:派 500 个员工(maxVUs 够用)
办事慢(224ms)
有些员工在办事,有些继续排队
员工去了 14,674 次
银行接待 837 人(5.7%)
效果:
- 员工够多
- 能持续排队
- 限流充分触发 ✅
测试 4:派 500 个员工,跑快点(完美)
rate=3000(更快)
员工去了 29,515 次
银行接待 829 人(2.8%)
效果:
- 压力更大
- 测试更准确
- 误差 0.12% 🎯
关键理解
1. 办事快 vs 慢
- 快(10ms):1 个员工 10 秒去 1000 次 ✅
- 慢(747ms):1 个员工 10 秒去 13 次 ❌
2. 员工够不够(重点!)
- 50 个员工:被阻塞,去 650 次 ❌
- 200 个员工:用完了,去 3,163 次 ❌
- 500 个员工:够用,去 14,674 次 ✅
3. 正在办理的也算
- 200 个员工,前 3 分钟用完
- 因为正在办理的占用了员工
- 没有空闲员工继续排队了
4. 银行规定不变
- 无论派多少员工
- 银行只接待 830 人!
七、总结
核心公式
总通过数 = rate × 时间 + burst
示例:rate=80r/s, burst=30, 10秒
理论:80 × 10 + 30 = 830 个
关键点
1. Nginx 很快(< 1ms)
2. VU 要等整个链路(包括 PHP)
3. 固定 VU 会被阻塞
4. 固定速率不受影响
5. 通过数由 Nginx 决定
测试目标
验证限流配置正确 ✅
测试限流效果 ✅
确保系统稳定 ✅
完成! 🎉

浙公网安备 33010602011771号