Files
photo-live/loadtest/README.md
T
浅念 f71a887f17 test(loadtest),docs: monitoring, cdn checklist and k6 framework
- monitoring.md: 指标→实现映射(1Panel+云监控+结构化日志,不引入额外监控
  系统);pino 字段表如实列自代码(访问日志 req.id/res.statusCode/
  responseTime + sync/worker 自定义 msg 字段);六类告警 jq 检索语句与
  阈值建议总表;日志保留(compose json-file 10m×3 已设 + 每日归档建议)
- cdn-checklist.md: 全步骤待凭证执行;图片加速三选一架构核对(双层签名
  冲突分析,推荐 c 起步=CDN 仅加速 viewer 静态、presigned 直链不变,
  图片收益后评估 b=CDN 鉴权+私有桶回源授权);缓存规则对齐源站 immutable;
  失效/预热 CLI 样例与多地域冷缓存测试点模板
- loadtest/: k6 框架——read-feed.js(N 观众四 mix:latest/hot 分页+大图+
  点赞+浏览,ramp 阶段)、upload-burst.js(M 并发上传全链含 complete 与
  发布/同步可选轮询,兼作数据种子)、lib/helpers.js(登录/建册/上传全链
  +内嵌 JPEG fixture 与 prod-smoke 同源)、README(运行方式/参数表/
  P95 输出解读对齐计划 §6 候选指标;D05 未定→阈值占位注释)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-21 01:52:47 +08:00

97 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# loadtest —— k6 压测框架(P5-prep Task 3)
对齐项目计划 §6 首轮容量验证建议的 L1/L2 阶梯(L1 基础:相册 1000 照片 /
3 人上传 / 100 观众;L2 目标候选:10000 / 10 / 1000;L3 扩展探测不设默认门槛)。
**D05(数量并发与网络目标)未定——所有 thresholds 为占位注释**,确认后取消
注释启用(候选口径见 §3)。
## 文件
| 文件 | 用途 |
| --- | --- |
| `read-feed.js` | 观众读路径:N 观众(ramp 阶段)持续分页读最新 + 热门切换 + 大图请求 + 点赞批次 + 浏览上报四 mix |
| `upload-burst.js` | 工作人员 M 并发小图上传全链(presign → OSS PUT → complete),可选轮询发布可见 / ov 同步;兼作数据种子 |
| `lib/helpers.js` | 登录 / 建相册 / 上传全链 / 分页 / 点赞 / 浏览 / 轮询助手 + 内嵌 800x600 JPEG fixture(与 `scripts/prod-smoke.sh` 同源) |
安装:[k6](https://grafana.com/docs/k6/latest/set-up/install/) ≥ 0.45(Windows/
macOS/Linux 均有安装包)。脚本在仓库根 `loadtest/` 下直接 `k6 run`。
## 运行方式
```bash
# ① 种子:建时间戳相册并上传照片(L1 档:3 人 × 20 张;setup 会打印 ALBUM_TOKEN)
k6 run --env BASE_CN=https://photo.example.cn \
--env ACCOUNT=admin --env PASSWORD='…' \
--env VUS=3 --env ITERATIONS=20 upload-burst.js
# ② 观众读路径压测(L1 档:100 观众;用 ① 打印的 ALBUM_TOKEN)
k6 run --env BASE=https://photo.example.com --env ALBUM_TOKEN=<打印值> \
--env VUS=100 --env STEADY=5m read-feed.js
# ②' L2 档:观众 1000 / 上传 10 人(先种更大相册:10 × 100)
k6 run --env BASE=… --env ALBUM_TOKEN=… --env VUS=1000 --env STEADY=10m read-feed.js
# ③ 上传链含发布观测(传输完成→发布;ov 同步端到端可选)
k6 run --env BASE_CN=… --env ACCOUNT=… --env PASSWORD=… \
--env ALBUM_ID=<albumId> --env ALBUM_TOKEN=<token> \
--env WAIT_PUBLISH=1 --env WAIT_SYNC=1 --env BASE_OV=https://photo.example.com \
upload-burst.js
# 结果归档:--out json=results-$(date +%F-%H%M).json 或 --summary-export=summary.json
```
### 参数总表(--env KEY=VALUE)
| KEY | 脚本 | 缺省 | 说明 |
| --- | --- | --- | --- |
| `BASE` | 两者 | 必填(read)/ upload 的 BASE_CN 回退 | 被测 API 基址(read 用 ov 域,upload 用 cn 域),末尾不带 `/v1` |
| `ALBUM_TOKEN` | read 必填 / upload 可选 | — | 公开相册 token(种子模式首次运行打印) |
| `ACCOUNT` / `PASSWORD` | upload | 必填 | 工作人员(或 admin)账号 |
| `ALBUM_ID` | upload | 空=建新册 | 复用相册;配合 `ALBUM_TOKEN` 才有发布轮询 |
| `VUS` | 两者 | 100 / 3 | 观众数 / 上传并发人数 |
| `ITERATIONS` | upload | 20 | 每人上传张数(L1 合计 = VUS×ITERATIONS) |
| `STEADY` / `RAMP_UP` / `RAMP_DOWN` | read | 5m / 1m / 1m | ramp 阶段时长 |
| `PAGES` / `PAGE_LIMIT` | read | 3 / 30 | 翻页深度 / 页大小(契约上限 60) |
| `HOT_SHARE` / `BIGIMG_SHARE` / `LIKE_SHARE` | read | 0.5 / 0.3 / 0.4 | 迭代内行为混合概率 |
| `THINK_MIN` / `THINK_MAX` | read | 0.5 / 3 | 观众思考时间秒 |
| `WAIT_PUBLISH` / `WAIT_SYNC` / `BASE_OV` | upload | 0 / 0 / 空 | 公开可见轮询开关(cn 发布 / ov 同步) |
| `POLL_TIMEOUT` / `POLL_INTERVAL` | upload | 60 / 2 | 轮询上限/间隔秒 |
## 输出解读(P95 关注点 ←→ 项目计划 §6 候选指标)
| 候选指标(计划原文) | k6 指标名 | 说明 |
| --- | --- | --- |
| 普通列表 API P95 < 500ms | `feed_list_duration` / `feed_hot_duration` | 按 tag `list_public_photos(_hot)` 分组的 Trend;「首批照片 P95<3s」是浏览器端口径(含渲染/图载),k6 以首页列表请求近似 + §冷缓存另测 |
| 传输完成到发布 P95 < 10s | `publish_wait_duration` | `WAIT_PUBLISH=1` 时 complete → cn 公开列表可见的等待;上传耗时(`upload_oss_put_duration`)单独统计,不计入处理时延(计划注记) |
| (同上,跨境) | `ov_sync_wait_duration` | `WAIT_SYNC=1`:complete → ov 公开可见(含推送 2s 间隔) |
| 点赞确认及广播 P95 < 2s | `like_confirm_duration` | API 确认口径;WS 广播时延属前端观测面,k6 不覆盖 |
| — | `upload_presign_duration` / `upload_complete_duration` | 上传链分步;`upload_chain_duration` 为三步端到端 |
| — | `like_rejected_total` / `publish_timeout_total` | 非目标信号:前者多为限速触发(见下),后者=发布超时需查 worker |
通用看板项:`http_req_duration`(按 name tag 细分)、`http_req_failed`、
`checks` 通过率、VU 数与迭代吞吐(`iteration_duration` 倒数)。**冷缓存/地域
时延不在 k6 范围**——用 `infra/deploy/cdn-checklist.md` §6 的多地域 curl 记录表。
## 阈值占位(D05 确认后启用)
两脚本 `options.thresholds` 均为注释占位(候选值即上表)。确认 D05 后:
1. 取消注释并按定案值调整;
2. L2 档跑 `k6 run … --tag testid=l2-$(date +%F)` 便于结果对比归档;
3. 阈值失败会让 k6 以非零码退出——可直接作为 P5 容量报告的机器判定。
## 操作注意(如实阅读再跑)
- **限速会污染指标**:likes 有 Redis 滑窗限速(`LIKE_IP_WINDOW_DELTA=2000` /
24h / IP;`LIKE_SESSION_WINDOW_DELTA=500` / 会话)——高并发点赞从单一压测机
出发很快触发 429(计入 `like_rejected_total`,且账面不是真实服务容量)。
staging 压测调大对应 env 或 `LIKE_SHARE=0`;需要真实验证限速本身时反向利用。
visits 的键含 visitorId(每 VU 唯一),`VISIT_WINDOW_REQUESTS=60`/窗 通常够用。
- **反代后 IP 归并**:经 OpenResty 反代时服务端 req.ip 是网关地址,trust proxy
未配置前所有限速按网关 IP 聚合——压测读数要意识到这一点(登记为 P5 项,
likes.controller.ts 文件头同注)。
- **对象存储是外部依赖**:`oss_put` / `oss_image_get` 打到 MinIO/OSS/CDN 直链,
其时延与被测 API 分开解读(tag 已区分);生产桶上跑会产生真实流量费用。
- **不自动清理**:种子照片留在相册内(时间戳标题可辨认),清理=后台软删或
`DELETE /v1/photos/{id}`(prod-smoke 同款);相册无删除端点。
- 对生产环境执行前必须获得明确许可——上传/点赞/浏览写路径全部落真实数据。