CC防护QPS容量怎么算?买多少才够

QPS容量买少了拦不住,买多了白花钱。本文讲清峰值口径怎么统一、日志和压测怎么算出真实水位、规格里的 QPS 与带宽怎么区分,以及余量和告警阈值该留多少。

Q:为什么「能扛多少 QPS」这句话不能直接信?

  • 同一个数字,口径能差十倍。跑静态小文件、后端全命中缓存、连接全复用,和跑一个要查三次数据库的 PHP 页面,是完全两回事。
  • 厂商标称通常按 7 层 HTTP 短请求 + 零后端依赖 测出来,你的真实业务能到标称的三成就算不错。
  • 更关键的是:QPS 高不高不重要,重要的是哪个接口被打了。首页 2 万 QPS 可能没事,登录接口 300 QPS 就能把数据库连接池打满。
  • 所以容量要按「接口」算,不是按「站点」算。一个站点里最贵的那三个接口,决定了你的真实容量。

Q:峰值 QPS 的三种口径差多少?

  • 1 秒粒度:最接近瞬时压力,但抖动大,一个爬虫突发就能刷出尖峰,做容量基线容易高估。
  • 5 秒或 1 分钟粒度:平滑后更接近后端真实负载,适合定扩容和告警。
  • 移动平均:适合看趋势,不适合定规格。
  • 实战做法是双口径并行:1 秒峰值定防护规格(设备要能扛瞬时),1 分钟均值定告警阈值(避免半夜乱响)。
  • 谈合同前必须和厂商对齐一句:你们的 QPS 是 1 秒峰值还是 5 分钟均值?这一句不问清,后面每个数字都会吵一遍。

Q:怎么从 Nginx 日志算出自己站的真实 QPS 分布?

  • 不用装任何监控,访问日志里就有答案。前提是日志格式带 $time_local 或 $msec。
# 每秒请求数 TOP 20($4 形如 [07/Nov/2026:05:00:01)
awk '{print $4}' /var/log/nginx/access.log | cut -c2-21 \
  | uniq -c | sort -rn | head -20

# 只要数字,算 99 分位
awk '{print $4}' /var/log/nginx/access.log | cut -c2-21 \
  | uniq -c | awk '{print $1}' | sort -n \
  | awk '{a[NR]=$1} END{print "p50="a[int(NR*0.5)], "p99="a[int(NR*0.99)], "max="a[NR]}'
  • 按接口拆开看,才知道钱该花在哪:
# 每秒请求数最高的 URI(去掉参数)
awk '{split($7,a,"?"); print $4" "a[1]}' /var/log/nginx/access.log \
  | cut -c2-21,22- | sort | uniq -c | sort -rn | head -20
  • 三个数记下来就够用:全天最大 1 秒 QPS、近 14 天同时段均值、TOP 3 接口各自的占比。
  • 平均值会骗人。一天 8 万 PV 的站,均值不到 1 QPS,但晚八点的 1 秒峰值可能是 200。

Q:自己的安全水位怎么测出来?

  • 别在生产环境压。用同配置的预发环境,或者低峰期在源站压,压完立刻回滚配置。
# 逐步加压:并发从 50 到 500,看 QPS 什么时候不再涨
wrk -t4 -c50  -d60s --latency https://example.com/list
wrk -t4 -c200 -d60s --latency https://example.com/list

# 同时盯后端,别只看压测端
# nginx: stub_status 的 active / reading / writing
# php-fpm: php-fpm -tt 看 max_children 是否打满
# mysql: show processlist 看 Sleep 与 Query 堆积
  • 拐点的判据不是 QPS 掉,而是QPS 不再涨、P95 延迟翻倍、5xx 开始出现。这个点就是安全水位。
  • 算一下理论值对照:并发连接 ≈ QPS × 平均响应时间。200 并发、平均 200ms,理论上限约 1000 QPS。压出来远低于这个数,说明瓶颈在后端不在入口。
  • 压测结果只对当次配置有效。加了索引、开了缓存、改了 PHP-FPM 进程数,水位就变了,要重测。

Q:七层的 QPS 和四层的带宽、并发是一回事吗?

  • 不是,而且经常被混着卖。四层设备卖的是 带宽 Gbps + PPS + 并发连接数 + 新建连接数,七层设备才卖 QPS。
  • 七层设备真正吃紧的是 新建连接速率(CPS) 与 SSL 握手能力,不是单纯 QPS。短连接攻击能把 CPS 打满,QPS 看着不高。
  • CDN 和高防的报价单上常见三种配额:带宽、QPS、请求数。三者独立计费,任何一项超了都可能限速或加钱。
  • 问清四件事:QPS 是整站还是单域名?超配是限速还是额外计费?回源 QPS 算不算在内?静态资源请求计不计入?

Q:缓存命中率会怎样改变容量结论?

  • 影响巨大。边缘 20 万 QPS、命中率 95%,源站只需承接 1 万 QPS 回源,再加一层本地缓存,实际落到 PHP 的可能只有两千。
  • 所以容量要分段算:边缘容量、回源容量、应用容量、数据库容量,四段里最窄的那一段就是真实上限。
  • CC 攻击恰恰专打最后两段。攻击者刷动态接口、带随机参数让缓存永不命中,边缘看着很闲,源站已经排队了。
  • 结论:缓存是容量的一部分,但攻击会把它抽走。做容量规划时至少要按命中率减半来估一次最坏情况。

Q:买的时候该留多少余量?

  • 基线取近 14 天同时段峰值的 1.5 到 2 倍,大促或推广期单独按活动预估峰值再乘 1.5。
  • 再往上留 30% 的突发额度。攻击的第一分钟永远是尖峰,没有余量就是直接被穿透。
  • 不要按攻击峰值买。攻击可以无限大,按攻击峰值买是无底洞;防护买的是「撑住常用业务 + 争取处置时间」。
  • 钱不够时的优先级:保住登录、下单、支付三个接口,其余接口降级或排队。这套取舍比多买 20% 规格更管用。

Q:限流阈值和买的规格怎么配合?

  • 原则只有一句:限流阈值必须低于设备规格上限。规格 5000 QPS,总闸就设 3000,让限流先动手,别让设备先撞墙。
  • 总闸用固定键而不是 IP 键,这样全站总量才控得住:
# 全站总闸:key 固定,所有请求进同一个桶
limit_req_zone "global" zone=total:10m rate=800r/s;

# 贵接口单独一个桶,按 IP 分
limit_req_zone $binary_remote_addr zone=login:10m rate=3r/s;

server {
    location / {
        limit_req zone=total burst=200 nodelay;
        limit_req_status 429;
    }
    location = /api/login {
        limit_req zone=login burst=5 nodelay;
        limit_req_status 429;
    }
}
  • burst 要留一点,否则正常用户连点两下就被拦,投诉比攻击更烦人。
  • 返回 429 而不是 444:444 直接断连,埋点和误杀统计全部丢失,事后没法复盘。
  • 限流阈值不是买来的,是压出来的。水位测出来 1000,总闸就设 700 到 800。

Q:容量告警阈值怎么设才不吵?

  • 双条件触发:QPS 超过基线 2 倍 且 P95 延迟超过基线 3 倍,持续 5 分钟。单看 QPS 会在大促时误报,单看延迟又太晚。
  • 再加两条兜底:5xx 比例超过 1%,以及后端连接池使用率超过 80%。
  • 告警分级:一级只发群通知,二级打电话。凌晨两点把人叫醒的次数要严格控制,不然第三次就没人接了。
  • 扩容有交付时间。云上弹性扩容几分钟,加带宽要工单,加高防规格要商务流程。按最慢的那条链路预留时间,别等打起来了才下单。

Q:还有什么补充建议?

  • 把「容量」写成一页纸:每个核心接口的基线 QPS、安全水位、限流阈值、降级开关。出事时照纸执行,不靠回忆。
  • 每季度重测一次。业务在长,代码在改,去年的水位今年一定不准。
  • 买防护时优先选「可临时升配」的,攻击这两小时能顶上去,比全年买大规格划算得多。
  • 别忽略静态资源:图片、下载、视频走独立域名和 CDN,不为它们买应用层 QPS,能省一半预算。