135 lines
6.9 KiB
Markdown
135 lines
6.9 KiB
Markdown
|
|
---
|
|||
|
|
tags: []
|
|||
|
|
date created: 星期五, 十一月 21日 2025, 3:54:54 下午
|
|||
|
|
date modified: 星期一, 十一月 24日 2025, 4:32:30 下午
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 2.3.7 性能指标遥测通道 (Performance Telemetry Channel)
|
|||
|
|
|
|||
|
|
遵循三阶段模型,我们深入探讨 **2.3.7 性能指标遥测通道 (Performance Telemetry Channel)**。
|
|||
|
|
|
|||
|
|
这是系统的“听诊器”。正如您所嘱咐,这个模块必须兼具**健壮性**(绝不因为发体检报告而把干活的人累死)和**丰富性**(体检报告不能只说“活着”,而要包含详细的心电图、血氧等指标)。
|
|||
|
|
|
|||
|
|
在雷达每秒处理数万脉冲的高压环境下,简单的“打印日志”或“直接发事件”都会瞬间压垮 CPU。我们必须采用**工业级遥测架构**。
|
|||
|
|
|
|||
|
|
## 一、 约束输入与对齐 (Constraints & Alignment)
|
|||
|
|
|
|||
|
|
基于高频实时系统的特性,我们需对齐以下硬性约束:
|
|||
|
|
|
|||
|
|
1. **零干扰 (Zero Interference)**:业务线程(如信号处理)更新指标时,必须是**无锁 (Lock-free)**、**无系统调用 (No Syscall)**、**无堆内存分配 (No Alloc)** 的。任何锁竞争都会导致处理抖动。
|
|||
|
|
2. **数据丰富性 (Richness)**:不仅需要简单的计数(Counter),还需要瞬时值(Gauge)和分布统计(Histogram,用于计算 P99 延迟)。
|
|||
|
|
3. **故障隔离 (Isolation)**:如果监控模块卡死或事件总线堵塞,业务线程必须能自动丢弃指标数据,绝不能被阻塞(Backpressure needs to be lossy for telemetry)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 二、 权衡分析与选项呈现 (Trade-off Matrix)
|
|||
|
|
|
|||
|
|
### 议题 1:数据上报模型 (Reporting Model)
|
|||
|
|
|
|||
|
|
|**选项**|**A. 立即推送 (Fire-and-Forget)**|**B. 线程本地聚合 + 定期刷新 (TLS Aggregation) (推荐)**|
|
|||
|
|
|---|---|---|
|
|||
|
|
|**机制**|每次 `counter++` 就 `publishAsync` 一个事件。|业务线程只更新本地变量 (`thread_local`)。后台定时器每秒收集一次并打包发送。|
|
|||
|
|
|**开销**|**极高**。每秒触发数万次事件总线入队操作,导致上下文切换风暴。|**极低**。热路径上仅是一次内存自增指令 (`INC`)。|
|
|||
|
|
|**实时性**|实时。|准实时(秒级延迟)。但在监控场景下完全可接受。|
|
|||
|
|
|**健壮性**|低。总线易过载。|**高**。将高频数据降频为低频快照。|
|
|||
|
|
|
|||
|
|
### 议题 2:统计数据结构 (Metric Data Structure)
|
|||
|
|
|
|||
|
|
|**选项**|**A. 简单原子变量 (Simple Atomic)**|**B. 多维带标签指标 (Tagged Multi-dimensional) (推荐)**|
|
|||
|
|
|---|---|---|
|
|||
|
|
|**机制**|全局 `std::atomic<int> g_packet_count;`|`Metrics::Counter("packet_recv", {{"channel", "1"}})->inc();`|
|
|||
|
|
|**丰富度**|**低**。只能看总数,无法区分通道、流或具体错误码。|**高**。支持维度下钻(Drill-down),能精确定位是哪个通道在丢包。|
|
|||
|
|
|**性能**|高。|需优化。若每次查找 Map 会慢,需结合 **句柄缓存 (Handle Caching)** 技术。|
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 三、 基线确立与实施规范
|
|||
|
|
|
|||
|
|
为了达成“既要马儿跑,又要马儿不吃草”的效果,我们确立 **B. TLS 聚合** + **B. 多维指标体系** 为基线。
|
|||
|
|
|
|||
|
|
### 1. 核心架构:双层缓冲遥测系统
|
|||
|
|
|
|||
|
|
这是一个**读写分离**的设计:
|
|||
|
|
|
|||
|
|
- **业务层 (Writer)**:只通过 `ThreadLocal` 句柄极速写入,无锁。
|
|||
|
|
- **收集层 (Collector)**:`MetricRegistry` 定期(如 1Hz)遍历所有线程的 TLS,执行原子快照(Snapshot),生成 `MetricsUpdateEvent`。
|
|||
|
|
|
|||
|
|
### 2. 丰富指标类型定义 (Rich Metric Types)
|
|||
|
|
|
|||
|
|
我们在 `TelemetryClient` 中提供三种核心原语:
|
|||
|
|
|
|||
|
|
1. **Counter (计数器)**:
|
|||
|
|
|
|||
|
|
- _用途_:累计吞吐量、错误总数。
|
|||
|
|
- _特性_:只增不减。
|
|||
|
|
- _实现_:`std::atomic<uint64_t>`。
|
|||
|
|
|
|||
|
|
2. **Gauge (仪表盘)**:
|
|||
|
|
|
|||
|
|
- _用途_:队列深度、内存占用、当前温度。
|
|||
|
|
- _特性_:可增可减,只关心瞬时值。
|
|||
|
|
- _实现_:`std::atomic<int64_t>` 或 `std::atomic<double>`。
|
|||
|
|
|
|||
|
|
3. **Histogram (直方图)**:
|
|||
|
|
|
|||
|
|
- _用途_:**P99 延迟**、Kernel 执行耗时分布。
|
|||
|
|
- _特性_:统计数据落入不同区间的次数。
|
|||
|
|
- _实现_:**固定分桶 (Fixed Buckets)**。
|
|||
|
|
- _健壮性设计_:严禁使用动态扩容的 `std::vector`。预分配一组原子计数器(如 `<1ms`, `1-5ms`, `5-10ms`, `>10ms`)。这避免了热路径上的内存分配。
|
|||
|
|
|
|||
|
|
### 3. 无锁高性能实现规范 (Hot-Path Optimization)
|
|||
|
|
|
|||
|
|
为了让业务代码写得爽且快,我们引入 **Static Handle** 模式。
|
|||
|
|
|
|||
|
|
- **业务代码示例**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
void SignalProcessor::processFrame() {
|
|||
|
|
// 1. 获取句柄 (仅第一次调用时有哈希查找开销,之后是极速指针访问)
|
|||
|
|
static auto* latency_hist = Telemetry::GetHistogram("proc_latency_us", {{"module", "dsp"}});
|
|||
|
|
static auto* packet_cnt = Telemetry::GetCounter("packets_processed");
|
|||
|
|
|
|||
|
|
auto start = Now();
|
|||
|
|
// … 业务逻辑 …
|
|||
|
|
auto duration = Now() - start;
|
|||
|
|
|
|||
|
|
// 2. 更新指标 (热路径:仅涉及原子操作,耗时 < 10ns)
|
|||
|
|
packet_cnt->inc();
|
|||
|
|
latency_hist->observe(duration);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4. 遥测协议与健壮性保障
|
|||
|
|
|
|||
|
|
- **事件定义**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
struct MetricsUpdateEvent : public BaseEvent {
|
|||
|
|
// 使用扁平化 Map 传输快照,减少序列化开销
|
|||
|
|
// Key: "proc_latency_us{module=dsp,bucket=1-5ms}" -> Value: 1024
|
|||
|
|
std::unordered_map<std::string, double> metrics_snapshot;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- **收集与发送 (Collector Thread)**:
|
|||
|
|
- **频率**:默认 **1Hz**。
|
|||
|
|
- **兜底策略 (健壮性核心)**:
|
|||
|
|
- **TryLock**: 收集线程在抓取快照时,使用 `try_lock`。如果业务线程正在极其罕见地初始化指标(持有锁),收集线程**直接放弃本次采集**,而不是阻塞等待。宁可丢一个点,不可卡顿系统。
|
|||
|
|
- **Backpressure**: 发送 `MetricsUpdateEvent` 时,使用 `EventBus::publishAsync`。如果事件总线队列已满(监控模块处理不过来),**直接丢弃该事件**。监控数据允许有损,业务数据不行。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 总结:2.3 章节最终基线图谱
|
|||
|
|
|
|||
|
|
至此,我们完成了 **2.3 内部控制平面通信接口** 的全方位设计。这套神经系统既有雷霆手段(同步抢占),又有细腻心思(全链路追踪),还有强健体魄(无锁遥测)。
|
|||
|
|
|
|||
|
|
|**接口领域**|**核心基线**|**关键技术点**|
|
|||
|
|
|---|---|---|
|
|||
|
|
|**2.3.1 总线架构**|**混合双通道 (Sync/Async)**|`publishSync` (指令) vs `publishAsync` (状态)|
|
|||
|
|
|**2.3.2 追踪传递**|**TLS + 智能闭包捕获**|`TraceContextGuard`, 跨线程自动传递|
|
|||
|
|
|**2.3.3 生命周期**|**异步指令 + 超时闭环**|`StartModuleEvent` -> `ModuleRunningEvent`|
|
|||
|
|
|**2.3.4 故障恢复**|**依赖感知四步法**|Pause -> Stop -> Restart -> Resume|
|
|||
|
|
|**2.3.5 资源保护**|**四级热节流 + 迟滞控制**|温度触发,软件占空比 (`sleep`) 降温|
|
|||
|
|
|**2.3.6 热更新**|**2PC + RCU**|投票 -> 提交,原子指针替换配置|
|
|||
|
|
|**2.3.7 性能遥测**|**TLS 聚合 + 定期快照**|`Static Handle` 缓存,无锁热路径,有损发送|
|