第 16 章 · 跟练一:大促前架构评审(压测 + ADR)
类型:半日工作坊(约 3~4 小时)
角色:你任 首席架构师,向 ARB(架构评审委员会)汇报「贤紫社区电商 · 双 11 下单链路」扩容与保护方案。
前置:ch09 容量公式、ch13 SLO/ARB 流程、ch14 C4/ADR/容量表模板。
产出物(全部内嵌本章,无需外部 deliverables 目录):
- 容量估算工作表(§16.2)
- k6 压测脚本与结果分析(§16.3~16.4)
- ADR 三份完整模板(§16.6)
- ARB 答辩记录(§16.7)
- 大促 Runbook(§16.8)
16.0 工作坊环境准备
| 项 | 要求 |
|---|---|
| 集群 | 小紫云计算 staging 集群,Namespace staging-app |
| 服务 | order-api Deployment,可调副本数 |
| 工具 | k6、kubectl、Grafana 只读账号 |
| 权限 | 可改 staging 副本数,不可直接改 prod |
| 参考 | ch09 §9.3 压测方法、ch14 §14.9 容量表 |
检查命令
kubectl get deploy order-api -n staging-app
kubectl top pods -n staging-app -l app=order-api
16.1 业务背景(给定,勿改数字)
| 项 | 数值 |
|---|---|
| 日常 DAU | 80 万 |
| 大促 DAU | 200 万 |
| 大促窗口 | 2 小时完成 45% 当日订单 |
| 人均订单 | 0.4 单/天 |
| 秒杀毛刺系数 | 峰值分钟内 ×3(相对稳态下单 QPS) |
| 目标 SLA | 下单 P99 < 400ms,可用性 99.95%(ch13) |
| 当前生产 | order-api 3 副本 2C4G |
| 单 Pod 安全 QPS | 180(P99<300ms,错误<0.1%,ch09 基线) |
| 成本因子 | 每增 1 副本 2C4G ≈ 0.15 单位/月(ch09 §9.5) |
系统边界:本跟练聚焦 POST /api/v1/orders;支付回调、搜索为 Tier-1,可引用但不展开。
16.2 任务一:填写容量估算工作表
复制下表到你的提交物,逐项手算并填写「你的结果」列。
# 容量估算工作表 · 双 11 order-api 扩容
| 元数据 | 值 |
|--------|-----|
| 项目 | 贤紫社区电商双 11 |
| 作者 | (填写) |
| 日期 | |
| 关联 | ch09、ch13、ch14 §14.9 |
## A. 业务输入(给定)
| 项 | 值 |
|----|-----|
| 大促 DAU | 2,000,000 |
| 人均订单/天 | 0.4 |
| 2h 集中度 | 45% |
| SLO | 99.95%(21.6 min/月错误预算,ch13) |
## B. 流量计算(需手算)
| 步骤 | 公式 | 参考答案 | 你的结果 |
|------|------|----------|----------|
| 大促总订单量/天 | DAU × 人均 | 800,000 单 | |
| 2h 内订单量 | × 45% | 360,000 单 | |
| 2h 秒数 | 7200 | 7200 | |
| **稳态下单峰值 QPS** | 订单量/秒数 | **50 QPS** | |
| 秒杀毛刺 QPS | ×3 | **150 QPS** | |
| 下单占 API 总流量比 | 25% | — | |
| 网关等效 QPS | 50/0.25 | **200 QPS** | |
## C. 副本数(ch09 公式)
| 步骤 | 公式 | 参考答案 | 你的结果 |
|------|------|----------|----------|
| 理论副本(毛刺) | ceil(150/180×1.35) | **2** | |
| N+1 / 混合负载 | order-api 还承担查询等 | 需 >2 | |
| **ARB 决议副本** | 评审决定 | **6**(HPA max 10) | |
**为何不是 2 副本?** 答辩要点:
- 单服务混合 QPS(下单+查询+库存)实测可达 **120+**
- 大促需 **N+1**:任 1 Pod 故障仍扛毛刺
- 缩容窗口:大促后回到 3(见 Runbook §16.8)
- 成本增量:+3×0.15 = **+0.45 单位/月**(可接受 vs GMV)
## D. 中间件与 SLO
| 组件 | 毛刺 150 QPS 下评估 | 动作 |
|------|---------------------|------|
| MySQL 写 | ~150 TPS | 单库够;加慢查询告警 |
| Redis | 热点 SKU | 预热 + ch17 击穿防护 |
| DB 连接 | 6×20=120 | <350 ✅ |
| 错误预算 | 大促失败不得超 21.6min/月 | 限流+降级 ADR |
## E. 签字
| 架构 | SRE | DBA |
|------|-----|-----|
| | | |
16.3 任务二:staging k6 压测(分步实验)
Step 1:确认 staging 基线(3 副本)
kubectl scale deploy/order-api -n staging-app --replicas=3
kubectl rollout status deploy/order-api -n staging-app
Step 2:保存压测脚本 k6-order-create.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Counter, Trend } from 'k6/metrics';
const errors = new Counter('order_errors');
const duration = new Trend('order_duration');
export const options = {
scenarios: {
ramp: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '2m', target: 30 },
{ duration: '5m', target: 80 },
{ duration: '3m', target: 100 },
{ duration: '2m', target: 0 },
],
gracefulRampDown: '30s',
},
},
thresholds: {
http_req_failed: ['rate<0.01'],
http_req_duration: ['p(99)<400'],
order_errors: ['count<50'],
},
};
const BASE = __ENV.API_BASE || 'http://staging-api.shop.local';
export default function () {
const payload = JSON.stringify({
userId: 10000 + (__VU % 5000),
items: [{ skuId: 'SKU001', qty: 1 }],
});
const params = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${__ENV.TOKEN || 'staging-test-token'}`,
'X-Idempotency-Key': `${__VU}-${__ITER}-${Date.now()}`,
},
tags: { name: 'createOrder' },
};
const res = http.post(`${BASE}/api/v1/orders`, payload, params);
duration.add(res.timings.duration);
const ok = check(res, {
'status is 201': (r) => r.status === 201,
'has orderId': (r) => r.json('orderId') !== undefined,
});
if (!ok) errors.add(1);
sleep(0.1);
}
Step 3:执行压测
k6 run --out json=k6-result.json k6-order-create.js
# 或输出 HTML(需 k6 扩展)
# K6_WEB_DASHBOARD=true k6 run k6-order-create.js
Step 4:扩容到 6 副本复测
kubectl scale deploy/order-api -n staging-app --replicas=6
kubectl rollout status deploy/order-api -n staging-app
k6 run k6-order-create.js
Step 5:记录结果表(填入你的实测)
教学样本(3 副本)
| 阶段 | VU | 下单 QPS | P50 | P99 | 错误率 |
|---|---|---|---|---|---|
| 爬坡 2m | 30 | 95 | 85ms | 210ms | 0% |
| 稳态 5m | 80 | 168 | 120ms | 380ms | 0.3% |
| 峰值 3m | 100 | 195 | 145ms | 520ms | 1.2% |
教学样本(6 副本)
| 阶段 | VU | 下单 QPS | P50 | P99 | 错误率 |
|---|---|---|---|---|---|
| 稳态 5m | 80 | 172 | 95ms | 310ms | 0.1% |
| 峰值 3m | 100 | 198 | 110ms | 350ms | 0.2% |
结论写法(提交 ARB):
3 副本在 QPS>170 时 P99 突破 400ms SLO;6 副本在同等负载下 P99<400ms、错误率<0.1%,支撑 ADR-016-01。
截图清单:Grafana order-api RED 面板、HPA 副本数、k6 终端 summary。
16.4 任务三:APISIX 限流配置(staging 验证)
# apisix-route-order-limit.yaml(片段)
routes:
- uri: /api/v1/orders
methods: [POST]