docs: rewrite README and AGENTS.md etc..
This commit is contained in:
1 parent
cc64df3e70
commit
a9bf849697
22 files changed
+514
-665
No files matched your search
+28
-25
@@ -4,11 +4,11 @@
|
||||
|
||||
## 1. 设计目标
|
||||
|
||||
1. **任意时间段**:支持 5 分钟、15 分钟这类相对时间段,也支持任意绝对区间。
|
||||
2. **短时间段足够精细**:近 5 分钟精确到 1 秒,近 1 小时精确到 5 秒。
|
||||
3. **汇总精确**:“这段时间内的丢包率、p95”由累加器精确合并得出,**不是各点的平均**。
|
||||
4. **快**:常用的短时间段直接从内存读取;较长的时间段读取预聚合层,现场合并。
|
||||
5. **新增指标时前端不用改**:指标按 id 选取,按分组(preset)展示,含义和单位来自目录。
|
||||
1. 任意时间段:支持 5 分钟、15 分钟这类相对时间段,也支持任意绝对区间。
|
||||
2. 短时间段足够精细:近 5 分钟精确到 1 秒,近 1 小时精确到 5 秒。
|
||||
3. 汇总精确:“这段时间内的丢包率、p95”由累加器精确合并得出,不是各点的平均。
|
||||
4. 快:常用的短时间段直接从内存读取;较长的时间段读取预聚合层,现场合并。
|
||||
5. 新增指标时前端不用改:指标按 id 选取,按分组(preset)展示,含义和单位来自目录。
|
||||
|
||||
## 2. 时间段与步长
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
原始段文件(30 天)── 超出热层时间范围的 1s~5s 精度查询 → 从原始块现算(区间 ≤ 15 分钟)
|
||||
```
|
||||
|
||||
**选择数据源的规则**:在“覆盖该时间段”并且“粒度能整除步长”的各层中,选择**最粗**的一层,然后把累加器按步长现场合并。
|
||||
选择数据源的规则:在“覆盖该时间段”并且“粒度能整除步长”的各层中,选择最粗的一层,然后把累加器按步长现场合并。
|
||||
|
||||
示例:
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
| 同上,但区间 > 15 分钟 | 降级为 10s 层或 1m 层,并标记 `degraded: true` |
|
||||
| 所需的层已经过期 | 降级为下一个更粗的可用层,并标记 `degraded: true` |
|
||||
|
||||
**热层内存估算**:15 条线路 × 3600 秒 × 约 300 B(稀疏直方图 + 计数)≈ 16 MB。热层也是 LiveHub 实时推送的数据来源。
|
||||
热层内存估算:15 条线路 × 3600 秒 × 约 300 B(稀疏直方图 + 计数)≈ 16 MB。热层也是 LiveHub 实时推送的数据来源。
|
||||
|
||||
## 4. 指标选择
|
||||
|
||||
@@ -86,14 +86,15 @@ id 的格式为 `指标.统计量`,详见 [analysis.md](analysis.md) 第 4 节
|
||||
|
||||
`preset=udp,tcp`
|
||||
|
||||
分组在 `Backend.Analysis` 的注册表中声明,并通过 `/api/public/catalog/presets` 下发给前端。新增指标后,只要把它加进某个分组,前端就会自动展示。
|
||||
分组在 `Backend.Analysis` 的 `MetricRegistry.Presets` 中声明,并通过 `/api/public/catalog/presets` 下发给前端。新增指标后,只要把它加进某个分组,前端就会自动展示。
|
||||
|
||||
| 分组 | 指标 |
|
||||
| --- | --- |
|
||||
| `udp` | `loss.up`、`loss.down`、`loss.total`、`rtt.p50`、`rtt.p95`、`rtt.p99`、`jitter.up.p95`、`jitter.down.p95`、`loss.run_max`、`reorder`、`dup` |
|
||||
| `quic` | `loss.up`、`loss.down`、`loss.total`、`rtt.p50`、`rtt.p95`、`rtt.p99`、`jitter.up.p95`、`jitter.down.p95`、`loss.run.up_max`、`loss.run.down_max`、`reorder`、`dup` |
|
||||
| `udp` | 与 `quic` 相同,保留为旧链接的别名;连续丢包使用 `loss.run_max` |
|
||||
| `tcp` | `tcp.rtt.p50`、`tcp.rtt.p95`、`tcp.rtt.p99`、`tcp.rtt.p999`、`tcp.rtt.max`、`tcp.rtt.tail`、`tcp.slow`、`tcp.stall.per_min`、`tcp.stall_max`、`tcp.retrans`、`tcp.srtt.p95` |
|
||||
| `connect` | `connect.success`、`connect.tcp_ms.p95`、`connect.tls_ms.p95`、`connect.fail.timeout`、`connect.fail.rst`、`connect.fail.tls` |
|
||||
| `session` | `hold.resets.per_h`、`hold.lifetime_max`、`probe.endpoint_changes` |
|
||||
| `session` | `hold.resets.per_h`、`hold.lifetime_max` |
|
||||
| `scores` | `score.base`、`score.mc`、`score.hy2`、`score.reality` |
|
||||
|
||||
目录中每个 id 都附带 `unit`、`label`、`description`、`better`(`lower` 或 `higher`,用于前端配色),以及 `thresholds`(优、良、差三档阈值,由模型给出)。
|
||||
@@ -102,7 +103,7 @@ id 的格式为 `指标.统计量`,详见 [analysis.md](analysis.md) 第 4 节
|
||||
|
||||
```json
|
||||
{
|
||||
"agent_id": "agt_…",
|
||||
"line_id": "agt_…~prb_…", "agent_id": "agt_…", "probe_id": "prb_…", "ip_version": "ipv4",
|
||||
"from": "2026-09-28T10:00:00.000Z", "to": "2026-09-28T10:15:00.000Z",
|
||||
"step": "2s", "source": "hot", "live": true, "degraded": false,
|
||||
"t": [1759053600000, 1759053602000, 1759053604000],
|
||||
@@ -123,9 +124,9 @@ id 的格式为 `指标.统计量`,详见 [analysis.md](analysis.md) 第 4 节
|
||||
}
|
||||
```
|
||||
|
||||
- 时间轴 `t` 使用 Unix 毫秒,只出现一次;`values` 中每个数组的长度都与 `t` 相同,可以直接交给 uPlot 或 ECharts。**这是 [api.md](../api/api.md) 第 7.4 节“时间用 RFC 3339”规则的明确例外**,理由是减小体积、方便绘图;其余时间字段仍然使用 RFC 3339。
|
||||
- 时间轴 `t` 使用 Unix 毫秒,只出现一次;`values` 中每个数组的长度都与 `t` 相同,可以直接交给 uPlot 或 ECharts。这是 [api.md](../api/api.md) 第 7.4 节“时间用 RFC 3339”规则的明确例外,理由是减小体积、方便绘图;其余时间字段仍然使用 RFC 3339。
|
||||
- 某个桶没有数据时填 `null`(不填 0),前端据此绘制断线。
|
||||
- `summary` 是**整个时间段**的精确合并结果。`coverage` = 实际数据量 / 期望数据量,用来提示前端“数据是否完整”。
|
||||
- `summary` 是整个时间段的精确合并结果。`coverage` = 实际数据量 / 期望数据量,用来提示前端“数据是否完整”。
|
||||
- `gaps` 和 `events` 用于在曲线上做标注(断线、端点变化、时钟跳变等)。
|
||||
|
||||
## 6. 端点
|
||||
@@ -134,7 +135,7 @@ id 的格式为 `指标.统计量`,详见 [analysis.md](analysis.md) 第 4 节
|
||||
| --- | --- | --- |
|
||||
| `GET /api/public/overview` | 首页排行:每条线路在该时间段内的汇总,并支持排序 | `range`(默认 `5m`)、`preset` 或 `metrics`、`sort`、`order` |
|
||||
| `GET /api/public/lines/{line_id}/series` | 单条线路的时序 | `range` 或 `from/to`、`step`、`preset` 或 `metrics` |
|
||||
| `GET /api/public/compare` | 多条线路对比(时间轴对齐) | `agents`、`metric`(单个)、`range` 或 `from/to`、`step` |
|
||||
| `GET /api/public/compare` | 多条线路对比(时间轴对齐) | `lines`(线路 id,逗号分隔)、`metric`(单个)、`range` 或 `from/to`、`step` |
|
||||
| `GET /api/public/lines/{line_id}/distribution` | 该时间段内的分布(直方图、CDF) | `metric`(直方图类,如 `rtt`、`jitter.down`、`tcp.rtt`)、`range` 或 `from/to` |
|
||||
| `GET /api/public/lines/{line_id}/heatmap` | 时间 × 延迟桶的热力图(类似 smokeping) | `metric`、`range` 或 `from/to`、`step` |
|
||||
| `GET /api/public/catalog/presets` | 分组定义 | — |
|
||||
@@ -145,14 +146,14 @@ id 的格式为 `指标.统计量`,详见 [analysis.md](analysis.md) 第 4 节
|
||||
{
|
||||
"range": "15m", "from": "…", "to": "…",
|
||||
"lines": [
|
||||
{ "agent_id": "agt_…", "name": "jp-1", "online": true,
|
||||
{ "id": "agt_…~prb_…", "agent_id": "agt_…", "probe_id": "prb_…", "name": "jp-1", "online": true,
|
||||
"summary": { "loss.down": 0.1, "loss.up": 0.0, "rtt.p95": 48.0, "tcp.retrans": 0.1, "score.mc": 94 },
|
||||
"coverage": 1.0 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`compare` 的响应示例:`{ "t":[…], "values":{ "agt_a":[…], "agt_b":[…] }, "summary":{ "agt_a":0.1, "agt_b":0.4 } }`
|
||||
`compare` 的响应示例:`{ "t":[…], "values":{ "<line_a>":[…], "<line_b>":[…] }, "summary":{ "<line_a>":0.1, "<line_b>":0.4 } }`
|
||||
|
||||
`distribution` 的响应示例:`{ "metric":"rtt", "unit":"ms", "buckets":[{"le":40.0,"count":812}, …], "p50":44.2, "p95":61.0, "p99":140.2, "count":8990 }`
|
||||
|
||||
@@ -166,7 +167,7 @@ id 的格式为 `指标.统计量`,详见 [analysis.md](analysis.md) 第 4 节
|
||||
② SSE /api/public/live?topics=series:{id}:2s:udp,tcp → 每当原始块到达(约每 10 s),
|
||||
推送一批新的 2s 桶和最新的汇总
|
||||
event: series
|
||||
data: {"agent_id":"…","step":"2s","t":[…5 个],"values":{…},"summary":{…}}
|
||||
data: {"line_id":"…","step":"2s","t":[…5 个],"values":{…},"summary":{…}}
|
||||
③ 前端把新点追加到末尾,并丢弃超出窗口的旧点;用推送来的汇总替换原来的汇总
|
||||
|
||||
前端切换为“自定义 09:00–10:00”→ 只执行 ①(live=false)
|
||||
@@ -174,7 +175,7 @@ id 的格式为 `指标.统计量`,详见 [analysis.md](analysis.md) 第 4 节
|
||||
|
||||
- SSE 的订阅主题格式为 `series:{line_id}:{step}:{preset}`,只允许 ≤ 1m 的步长;更粗的步长由前端定时重新拉取即可。
|
||||
- `overview` 也可以跟随实时:订阅 `overview:{range}:{preset}`,每 10 s 推送一次最新汇总。
|
||||
- 实时延迟:取决于原始块的切块节奏(10 s)加上处理时间,**典型值为 10~12 s**。1s 步长的点会按批次到达,而不是每秒一个。
|
||||
- 实时延迟:取决于原始块的切块节奏(10 s)加上处理时间,典型值为 10~12 s。1s 步长的点会按批次到达,而不是每秒一个。
|
||||
|
||||
## 8. 缓存与限额
|
||||
|
||||
@@ -195,15 +196,17 @@ id 的格式为 `指标.统计量`,详见 [analysis.md](analysis.md) 第 4 节
|
||||
## 9. 实现位置
|
||||
|
||||
```text
|
||||
Backend.Api/Public/SeriesEndpoints.cs 参数解析、校验、缓存头(不含业务)
|
||||
Backend.Api/Public/QueryEndpoints.cs 参数解析、校验、缓存头(不含业务)
|
||||
Backend.Application/Query/
|
||||
├─ RangeResolver range 或 from/to 与 step → 对齐后的桶序列
|
||||
├─ Steps / Bucketing 常用步长与分桶
|
||||
├─ SourcePlanner 选择数据源(热层 / 10s / 1m / 5m / 1h / 原始块现算),判断是否降级
|
||||
├─ SeriesQuery 读取累加器 → 按步长合并 → 导出统计量 → 列式结果
|
||||
├─ SummaryQuery 整个时间段的精确合并(overview 和 series 共用)
|
||||
└─ DistributionQuery 合并直方图 → 分桶、CDF、热力图
|
||||
Backend.Application/Live/LiveHub 热层环形缓冲(1s,1 小时)+ SSE 主题
|
||||
Backend.Analysis 累加器的合并与导出(与持久化路径共用同一份代码)
|
||||
├─ RowLoader 读取累加器行
|
||||
├─ MetricSelection 按 id 或分组选取指标
|
||||
├─ QueryService series、overview、compare、distribution、heatmap
|
||||
└─ QueryResults 列式结果类型
|
||||
Backend.Application/Live/ HotLayer(1s 环形缓冲,1 小时)、HotLayerBackfill、LiveHub(SSE 主题)
|
||||
Backend.Analysis 累加器的合并与导出(与持久化路径共用同一份代码)
|
||||
```
|
||||
|
||||
新增指标时,这套查询代码不需要任何改动:`SeriesQuery` 只通过注册表按 id 读取累加器并导出值。
|
||||
新增指标时查询代码不需要改动:`QueryService` 通过注册表按 id 读取累加器并导出值。
|
||||
Reference in new issue
Block a user