/v1/deliver/{appKey}/{envKey},
端上分流跑编译产物 @addesk/core 的 applyTargeting,拓扑与素材取自 PostgreSQL。
「自检项」是每个场景跑完后在全量结果上重新判定的断言(不是拼接分片结论)。
5xx 与连接失败是硬红线:分发路径只读内存快照,没有任何理由抛 5xx。
延迟分两行报:服务端 P99 / 均值取自服务自己的 ta_deliver_duration_seconds 直方图,是被测系统的真实耗时;
各场景里另有一行「客户端实测」,那条含 Node 压测器的排队,数值高一个量级 —— 瓶颈在压测器,不在服务。
同一套拓扑、同一个服务进程,变的是请求形状。每行点开有该场景的完整断言与数据。
真实流量配比的对照组:后面九个场景的所有偏差都相对它解释。
| HTTP 状态 | 次数 | 占比 | |
|---|---|---|---|
200 | 9,999,999 | 100.0% |
| 延迟来源 | P50 | P90 | P99 | P99.9 | MAX | 均值 |
|---|---|---|---|---|---|---|
| 客户端实测 含 Node 压测器排队 |
6.16 | 7.52 | 12.97 | 16.05 | 43.83 | — |
| 服务端 handler Prometheus 直方图差值 |
≤0.5 | — | ≤0.5 | ≤1 | — | 0.024 |
吞吐 59,221 rps。两行差在压测器:单个 Node 客户端封顶约 25k rps,分片并行后排队体现在客户端那一行,服务端直方图才是被测系统的真实耗时。
每次请求都是没见过的 deviceId(装机潮/清缓存)。灰度与 A/B 分桶是 hash(设备),设备基数放大 20 倍才能暴露分桶偏斜。
| HTTP 状态 | 次数 | 占比 | |
|---|---|---|---|
200 | 9,999,999 | 100.0% |
| 延迟来源 | P50 | P90 | P99 | P99.9 | MAX | 均值 |
|---|---|---|---|---|---|---|
| 客户端实测 含 Node 压测器排队 |
6.51 | 8.08 | 13.73 | 16.88 | 45.52 | — |
| 服务端 handler Prometheus 直方图差值 |
≤0.5 | — | ≤0.5 | ≤1 | — | 0.025 |
吞吐 55,936 rps。两行差在压测器:单个 Node 客户端封顶约 25k rps,分片并行后排队体现在客户端那一行,服务端直方图才是被测系统的真实耗时。
只有 64 个 deviceId 反复请求(共享设备/测试机房)。同一设备必须永远拿到同一变体 —— 分流的确定性在这里才可证伪。
| HTTP 状态 | 次数 | 占比 | |
|---|---|---|---|
200 | 9,999,999 | 100.0% |
| 延迟来源 | P50 | P90 | P99 | P99.9 | MAX | 均值 |
|---|---|---|---|---|---|---|
| 客户端实测 含 Node 压测器排队 |
6.53 | 8.61 | 14.40 | 18.88 | 42.41 | — |
| 服务端 handler Prometheus 直方图差值 |
≤0.5 | — | ≤0.5 | ≤1 | — | 0.026 |
吞吐 54,922 rps。两行差在压测器:单个 Node 客户端封顶约 25k rps,分片并行后排队体现在客户端那一行,服务端直方图才是被测系统的真实耗时。
SDK 带 If-None-Match 回源。304 必须只在内容真的没变时返回;这里同时检验 304 与 200 的语义一致性。
| HTTP 状态 | 次数 | 占比 | |
|---|---|---|---|
304 | 7,001,184 | 70.0% | |
200 | 2,998,815 | 30.0% |
| 延迟来源 | P50 | P90 | P99 | P99.9 | MAX | 均值 |
|---|---|---|---|---|---|---|
| 客户端实测 含 Node 压测器排队 |
4.99 | 8.28 | 12.76 | 18.16 | 72.48 | — |
| 服务端 handler Prometheus 直方图差值 |
≤0.5 | — | ≤0.5 | ≤2.5 | — | 0.031 |
吞吐 69,300 rps。两行差在压测器:单个 Node 客户端封顶约 25k rps,分片并行后排队体现在客户端那一行,服务端直方图才是被测系统的真实耗时。
同一 URL、payload 走 X-TA-Payload 头,只有平台不同。响应随平台变化,那么 ETag / Vary 必须能区分,否则 CDN 会把 iOS 的包投给未知端。
| HTTP 状态 | 次数 | 占比 | |
|---|---|---|---|
200 | 9,999,999 | 100.0% |
| 延迟来源 | P50 | P90 | P99 | P99.9 | MAX | 均值 |
|---|---|---|---|---|---|---|
| 客户端实测 含 Node 压测器排队 |
4.58 | 7.73 | 11.51 | 16.89 | 45.39 | — |
| 服务端 handler Prometheus 直方图差值 |
≤0.5 | — | ≤0.5 | ≤1 | — | 0.013 |
吞吐 75,203 rps。两行差在压测器:单个 Node 客户端封顶约 25k rps,分片并行后排队体现在客户端那一行,服务端直方图才是被测系统的真实耗时。
SDK 常只要一两个入口(?entry=)。投影必须与整包子集完全一致,且 capacity 截断在投影下同样生效。
| HTTP 状态 | 次数 | 占比 | |
|---|---|---|---|
200 | 9,999,999 | 100.0% |
| 延迟来源 | P50 | P90 | P99 | P99.9 | MAX | 均值 |
|---|---|---|---|---|---|---|
| 客户端实测 含 Node 压测器排队 |
5.10 | 9.00 | 14.81 | 24.29 | 65.47 | — |
| 服务端 handler Prometheus 直方图差值 |
≤0.5 | — | ≤0.5 | ≤2.5 | — | 0.018 |
吞吐 64,736 rps。两行差在压测器:单个 Node 客户端封顶约 25k rps,分片并行后排队体现在客户端那一行,服务端直方图才是被测系统的真实耗时。
limit 是客户端可控输入:负数、0、超大、非数字。必须被夹到 [1,MaxLimit],且永不突破入口 capacity。
| HTTP 状态 | 次数 | 占比 | |
|---|---|---|---|
200 | 9,999,999 | 100.0% |
| 延迟来源 | P50 | P90 | P99 | P99.9 | MAX | 均值 |
|---|---|---|---|---|---|---|
| 客户端实测 含 Node 压测器排队 |
6.63 | 9.79 | 16.35 | 23.74 | 65.94 | — |
| 服务端 handler Prometheus 直方图差值 |
≤0.5 | — | ≤0.5 | ≤2.5 | — | 0.028 |
吞吐 52,727 rps。两行差在压测器:单个 Node 客户端封顶约 25k rps,分片并行后排队体现在客户端那一行,服务端直方图才是被测系统的真实耗时。
公网入口。坏 base64、超长字段、注入字符、巨大 tags 数组都必须是 4xx 或安全降级,绝不 5xx、绝不拖垮 P99。
| HTTP 状态 | 次数 | 占比 | |
|---|---|---|---|
200 | 8,199,615 | 82.0% | |
400 | 1,800,384 | 18.0% |
| 延迟来源 | P50 | P90 | P99 | P99.9 | MAX | 均值 |
|---|---|---|---|---|---|---|
| 客户端实测 含 Node 压测器排队 |
24.02 | 28.55 | 48.10 | 110.02 | 338.46 | — |
| 服务端 handler Prometheus 直方图差值 |
≤0.5 | — | ≤2.5 | ≤5 | — | 0.136 |
吞吐 15,321 rps。两行差在压测器:单个 Node 客户端封顶约 25k rps,分片并行后排队体现在客户端那一行,服务端直方图才是被测系统的真实耗时。
server 环境每次都要 HMAC 校验 + 时间窗。全量打签名环境,验证签名不是吞吐悬崖,并确认坏签名一律 401。
| HTTP 状态 | 次数 | 占比 | |
|---|---|---|---|
200 | 8,500,444 | 85.0% | |
401 | 1,499,555 | 15.0% |
| 延迟来源 | P50 | P90 | P99 | P99.9 | MAX | 均值 |
|---|---|---|---|---|---|---|
| 客户端实测 含 Node 压测器排队 |
6.59 | 9.09 | 14.88 | 19.88 | 44.44 | — |
| 服务端 handler Prometheus 直方图差值 |
≤0.5 | — | ≤0.5 | ≤2.5 | — | 0.032 |
吞吐 53,925 rps。两行差在压测器:单个 Node 客户端封顶约 25k rps,分片并行后排队体现在客户端那一行,服务端直方图才是被测系统的真实耗时。
前九个场景每设备一个 IP,刻意绕开限流以测业务逻辑。这一轮反过来:全部流量挤同一批 IP,验证限流器真的兜得住,且 429 带 Retry-After。
| HTTP 状态 | 次数 | 占比 | |
|---|---|---|---|
429 | 9,957,375 | 99.6% | |
200 | 42,624 | 0.4% |
| 延迟来源 | P50 | P90 | P99 | P99.9 | MAX | 均值 |
|---|---|---|---|---|---|---|
| 客户端实测 含 Node 压测器排队 |
3.70 | 5.31 | 8.37 | 12.43 | 46.01 | — |
| 服务端 handler Prometheus 直方图差值 |
≤0.5 | — | ≤0.5 | ≤2.5 | — | 0.075 |
吞吐 95,621 rps。两行差在压测器:单个 Node 客户端封顶约 25k rps,分片并行后排队体现在客户端那一行,服务端直方图才是被测系统的真实耗时。
每格是该场景实测曝光率,判定用二项分布 ±2σ 容差(曝光率是伯努利采样,样本越大容差越窄)。
只统计 percent 这一档 —— 用 excluded 中 reason === 'percent' 精确定位,
不把 tags / version 的淘汰混进来。
横着读能回答一件事:灰度比例是否与流量形状无关。设备全新、设备极窄、投影请求……都不该改变它。
| 投放组 | 配置 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 新春主视觉 A/B 手机点歌 |
100% | 100.0 | 100.0 | 100.0 | 100.0 | 100.0 | 100.0 | 100.0 | 100.0 | 100.0 | 100.0 |
| 唱后推荐 · 三素材灰度 手机点歌 |
50% | 50.0 | 50.0 | 50.0 | 50.1 | — | 50.0 | 50.0 | 50.0 | 50.0 | 49.9 |
| 播前贴片 A/B 家庭 VOD |
60% | 60.0 | 60.0 | 59.3 | 60.1 | — | 59.8 | 60.0 | 60.0 | — | 61.0 |
| 门店大促 A/B 线下 KTV |
100% | 100.0 | 100.0 | 100.0 | 100.0 | — | 100.0 | 100.0 | 100.0 | — | 100.0 |
| 车载首页热歌榜 A/B 车载 |
80% | 80.0 | 80.0 | 76.5 | 80.0 | — | 79.9 | 80.0 | 80.0 | 79.9 | 79.9 |
条形是实测分流比,数字是配置权重。有两个场景按设计就不该等于配置值,它们是对照而不是缺陷:
· 03 极窄设备集合 全场只有 64 个设备,一个设备对一个投放组只掷一次骰子 —— 样本是 64 不是千万,单臂偏几十个百分点是正常抖动;
· 07 越界 limit 会随机发 limit=1,服务端按 sort 截断,排在前面的变体必然被多取(实测平均下发 2.60 条/请求,基线是 3.00)。
其余八个场景必须落在配置比例上。
| 场景 | A | B | 实测分布 | 样本 | 判定 |
|---|---|---|---|---|---|
| 01 基线车队 | 59.9% | 40.1% | 60%40% |
1,230,902 | ✓ 与配置一致 |
| 02 全新设备洪峰 | 60.0% | 40.0% | 60%40% |
1,231,716 | ✓ 与配置一致 |
| 03 极窄设备集合 | 46.9% | 53.1% | 47%53% |
1,229,350 | 按设计偏离 |
| 04 ETag 重验证 | 60.1% | 39.9% | 60%40% |
369,482 | ✓ 与配置一致 |
| 06 按需入口投影 | 60.0% | 40.0% | 60%40% |
307,951 | ✓ 与配置一致 |
| 07 越界 limit | 68.0% | 32.0% | 68%32% |
1,229,503 | 按设计偏离 |
| 10 单 IP 限流 | 59.5% | 40.5% | 59%41% |
5,298 | ✓ 与配置一致 |
| 场景 | A | B | C | 实测分布 | 样本 | 判定 |
|---|---|---|---|---|---|---|
| 01 基线车队 | 50.1% | 30.0% | 19.9% | 50%30%20% |
2,050,933 | ✓ 与配置一致 |
| 02 全新设备洪峰 | 50.0% | 30.0% | 20.0% | 50%30%20% |
2,050,138 | ✓ 与配置一致 |
| 03 极窄设备集合 | 81.2% | 0.0% | 18.8% | 81%19% |
2,051,553 | 按设计偏离 |
| 04 ETag 重验证 | 50.1% | 30.0% | 19.9% | 50%30%20% |
616,415 | ✓ 与配置一致 |
| 06 按需入口投影 | 50.0% | 30.0% | 19.9% | 50%30%20% |
513,464 | ✓ 与配置一致 |
| 07 越界 limit | 60.1% | 24.0% | 15.9% | 60%24%16% |
2,049,390 | 按设计偏离 |
| 08 恶意 / 畸形 payload | 50.0% | 29.9% | 20.1% | 50%30%20% |
1,682,809 | ✓ 与配置一致 |
| 09 签名环境压力 | 49.7% | 29.8% | 20.5% | 50%30%21% |
2,126,602 | ✓ 与配置一致 |
| 10 单 IP 限流 | 50.4% | 29.9% | 19.7% | 50%30%20% |
8,672 | ✓ 与配置一致 |
| 场景 | A | B | 实测分布 | 样本 | 判定 |
|---|---|---|---|---|---|
| 01 基线车队 | 69.9% | 30.1% | 70%30% |
359,949 | ✓ 与配置一致 |
| 02 全新设备洪峰 | 70.0% | 30.0% | 70%30% |
359,284 | ✓ 与配置一致 |
| 03 极窄设备集合 | 97.4% | 2.6% | 97% |
355,505 | 按设计偏离 |
| 04 ETag 重验证 | 70.0% | 30.0% | 70%30% |
107,961 | ✓ 与配置一致 |
| 06 按需入口投影 | 70.1% | 29.9% | 70%30% |
119,429 | ✓ 与配置一致 |
| 07 越界 limit | 76.0% | 24.0% | 76%24% |
359,481 | 按设计偏离 |
| 08 恶意 / 畸形 payload | 70.1% | 29.9% | 70%30% |
295,411 | ✓ 与配置一致 |
| 10 单 IP 限流 | 69.9% | 30.1% | 70%30% |
1,546 | ✓ 与配置一致 |
| 场景 | A | B | 实测分布 | 样本 | 判定 |
|---|---|---|---|---|---|
| 01 基线车队 | 49.9% | 50.1% | 50%50% |
2,400,513 | ✓ 与配置一致 |
| 02 全新设备洪峰 | 50.0% | 50.0% | 50%50% |
2,400,015 | ✓ 与配置一致 |
| 03 极窄设备集合 | 53.1% | 46.9% | 53%47% |
2,400,113 | 按设计偏离 |
| 04 ETag 重验证 | 49.9% | 50.1% | 50%50% |
719,555 | ✓ 与配置一致 |
| 06 按需入口投影 | 50.0% | 50.0% | 50%50% |
799,892 | ✓ 与配置一致 |
| 07 越界 limit | 50.6% | 49.4% | 51%49% |
1,947,921 | 按设计偏离 |
| 08 恶意 / 畸形 payload | 50.0% | 50.0% | 50%50% |
1,967,733 | ✓ 与配置一致 |
| 10 单 IP 限流 | 49.6% | 50.4% | 50%50% |
10,302 | ✓ 与配置一致 |
| 场景 | A | B | 实测分布 | 样本 | 判定 |
|---|---|---|---|---|---|
| 01 基线车队 | 64.9% | 35.1% | 65%35% |
879,824 | ✓ 与配置一致 |
| 02 全新设备洪峰 | 65.0% | 35.0% | 65%35% |
879,812 | ✓ 与配置一致 |
| 03 极窄设备集合 | 63.3% | 36.7% | 63%37% |
841,724 | 按设计偏离 |
| 04 ETag 重验证 | 64.8% | 35.2% | 65%35% |
263,302 | ✓ 与配置一致 |
| 06 按需入口投影 | 64.7% | 35.3% | 65%35% |
292,436 | ✓ 与配置一致 |
| 07 越界 limit | 64.9% | 35.1% | 65%35% |
705,225 | 按设计偏离 |
| 08 恶意 / 畸形 payload | 65.1% | 34.9% | 65%35% |
721,682 | ✓ 与配置一致 |
| 09 签名环境压力 | 64.9% | 35.1% | 65%35% |
3,396,929 | ✓ 与配置一致 |
| 10 单 IP 限流 | 64.6% | 35.4% | 65%35% |
3,672 | ✓ 与配置一致 |
服务端下发 309,338,242 条,端上最终留下 158,651,558 条。服务端只做授权 / 启停 / 排序 / 平台粗筛 / 容量截断,精细控制在端上按 platform → version → tags → percent → A/B → weight 顺序裁剪。
| 原因 | 含义 | 次数 | 占比 | |
|---|---|---|---|---|
ab | A/B 分流:同组只保留命中的那个变体 | 53,328,098 | 35.4% | |
tags | 标签不匹配 payload.tags | 52,082,049 | 34.6% | |
percent | 灰度未命中 | 45,276,537 | 30.0% |
| 状态 | 语义 | 次数 | 占比 |
|---|---|---|---|
200 | 正常下发 | 79,741,492 | 79.7% |
429 | 单 IP 限流(场景 10) | 9,957,375 | 10.0% |
304 | 内容未变,命中客户端缓存 | 7,001,184 | 7.0% |
400 | 畸形 payload 被拒(场景 08) | 1,800,384 | 1.8% |
401 | 签名缺失 / 错误 / 过期(场景 09) | 1,499,555 | 1.5% |