创建仓库

This commit is contained in:
2025-12-11 07:24:36 +08:00
commit 0d81c1792d
128 changed files with 15104 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
---
tags:
aliases:
- 2.3 内部控制平面通信接口 (Internal Control Plane Interface - IPC)
date created: 星期四, 十一月 20日 2025, 11:47:20 晚上
date modified: 星期四, 十一月 20日 2025, 11:48:22 晚上
---
# 2.3 内部控制平面通信接口 (Internal Control Plane Interface - IPC)
- **覆盖范围**:定义系统内部各功能模块(`IModule`)与核心管理组件(调度器、配置管理器)之间的**控制流交互机制**。该接口基于**进程内事件总线In-Process EventBus**架构,实现模块间的解耦、生命周期编排、资源仲裁及故障传递。**核心约束**:控制平面严禁传输任何业务数据块(如 I/Q 数据或点迹数组),仅允许传输元数据、状态码和控制指令。
- **2.3.1 事件总线架构与路由机制 (Event Bus Architecture & Routing Mechanism)**
- **核心指向**:定义系统控制流的中枢神经。采用**发布 - 订阅 (Pub/Sub)** 模式,实现 `IEventBus` 接口。支持**同步分发**`publishSync`,用于高优先级指令的即时回调)与**异步分发**`publishAsync`,用于状态上报的非阻塞入队)的混合路由策略,确保控制指令在微秒级内准确送达。
- **2.3.2 全链路追踪上下文传递 (Trace Context Propagation)**
- **核心指向**定义控制指令的审计与追踪规范。强制要求所有控制事件Event必须携带全局唯一的 `TraceID`。涵盖在跨线程(如从 `API网关` 线程到 `SignalProcessor` 工作线程)传递事件时,利用 `TraceContextGuard` 或类似的 **RAII 机制**自动捕获、保存和恢复线程本地存储TLS中的追踪上下文实现“无感”的链路追踪。
- **2.3.3 生命周期编排与状态同步协议 (Lifecycle Orchestration & State Synchronization)**
- **核心指向**:定义 `TaskScheduler` 与业务模块间的握手协议。涵盖标准化的生命周期指令事件(`StartModuleEvent`, `StopModuleEvent`, `PauseModuleEvent`)以及模块的状态变更回执(`ModuleRunningEvent`, `ModuleStoppedEvent`)。重点关注在系统启动/关闭时的**拓扑依赖顺序**控制逻辑,确保无“悬空”状态。
- **2.3.4 故障传播与恢复信令 (Fault Propagation & Recovery Signaling)**
- **核心指向**:定义异常情况下的通信契约。涵盖**致命错误上报**`ModuleFailedEvent`,携带标准化 `ErrorCode` 和堆栈快照)的格式,以及调度器下发的**恢复指令流**(如 `PauseDataFlow` -> `RestartModule` -> `ResumeDataFlow`)的时序规范。集成**熔断器Circuit Breaker**状态广播,防止故障扩散。
- **2.3.5 资源仲裁与抢占式优先级控制 (Resource Arbitration & Preemptive Priority Control)**
- **核心指向**:针对 CPU/GPU 异构计算资源的动态协调接口。涵盖由 `ResourceCoordinator` 发出的强制性指令(如 `SetComputePriorityEvent(LOW/HIGH)`),以及业务模块在收到指令后切换 **CUDA 流优先级** 或执行 **任务分片Task Slicing** 避让的响应时限要求(如 < 10ms)。
- **2.3.6 两阶段配置热更新协议 (Two-Phase Configuration Hot-Reload Protocol)**
- **核心指向**定义动态配置变更时的协商机制涵盖 `ConfigManager` 发起的 **验证询问”**`ValidateConfigChangeEvent`模块需在超时前反馈可行性 **变更通知”**`ConfigChangedEvent`模块执行原子更新确保在并发环境下配置更新的事务一致性
- **2.3.7 性能指标遥测通道 (Performance Telemetry Channel)**
- **核心指向**定义业务模块向 `MonitoringModule` 上报健康数据的单向通道涵盖 `MetricsUpdateEvent` 的数据结构定义键值对映射以及采用 **线程本地缓存Thread-Local Storage** 结合 **MPSC多生产单消费队列** 的高吞吐无锁上报策略彻底消除监控逻辑对业务主线程的锁竞争干扰

View File

@@ -0,0 +1,120 @@
---
tags: []
date created: 星期四, 十一月 20日 2025, 11:58:41 晚上
date modified: 星期四, 十一月 20日 2025, 11:59:25 晚上
---
# 2.3.1 事件总线架构与路由机制 (Event Bus Architecture & Routing Mechanism)
## 一、 约束输入与对齐 (Constraints & Alignment)
基于您提供的设计文档(特别是 `05_任务调度器设计.md`)和系统环境,我们面临以下**硬性约束**
1. **进程内通信 (In-Process)**:本节讨论的是同一个进程(`main_app`)内部,不同 C++ 对象(模块)之间的交互。**严禁**引入 Socket、Pipe 或由 OS 调度的 IPC 机制(如 DBus/ZMQ以避免微秒级的系统调用开销。
2. **语言标准**:必须兼容 **C++14** (GCC 7.3)。
3. **实时性要求**:控制指令(如 `StopModule`)必须在 **\< 1ms** 内到达目标模块。
4. **全链路追踪**:事件总线是 `TraceID` 传递的关键载体,必须支持上下文的自动传播。
-----
## 二、 权衡分析与选项呈现 (Trade-off Matrix)
### 议题 1路由分发策略 (Dispatch Strategy)
| 选项 | A. 纯同步直接调用 (Synchronous Direct) | B. 纯异步队列 (Asynchronous Queued) | C. 混合双通道 (Hybrid Dual-Channel) **(推荐)** |
| :--- | :--- | :--- | :--- |
| **机制** | `publish()` 时直接在**调用者线程**遍历并执行所有回调函数。 | `publish()` 将事件推入队列。后台线程池异步取出并执行回调。 | 提供 `publishSync`(高优指令)和 `publishAsync`(状态上报)两个接口。 |
| **延迟** | **最低 (微秒级)**。无上下文切换,无排队。 | **较高**。受队列深度和调度器负载影响。 | **灵活**。关键指令零延迟,非关键消息不阻塞主业务。 |
| **死锁风险** | **高**。如果回调函数里又发了新事件,容易导致递归死锁。 | **低**。解耦了生产者和消费者。 | **中**。需规范同步通道的使用场景。 |
| **适用场景** | 紧急停止、资源抢占。 | 日志上报、非关键状态更新。 | **生产环境标准解**。 |
### 议题 2订阅者模型 (Subscriber Model)
| 选项 | A. 泛型/模板回调 (Type-Erasure) **(推荐)** | B. 继承接口 (Inheritance) |
| :--- | :--- | :--- |
| **机制** | `bus->subscribe<MyEvent>(lambda)`。利用 `std::function``std::type_index`。 | 订阅者必须实现 `IEventHandler<MyEvent>` 接口。 |
| **耦合度** | **极低**。模块不需要继承特定基类,只要函数签名对就行。 | **高**。侵入性强,增加类层级复杂度。 |
| **灵活性** | **高**。支持 Lambda便于捕获 `this` 指针或上下文。 | 低。 |
| **性能** | 极高(现代编译器优化 `std::function` 很好)。 | 虚函数调用开销(微小)。 |
-----
## 三、 基线确立与实施规范
为了兼顾雷达系统对**指令的即时响应**(如资源抢占)和**状态处理的高吞吐**(如海量模块状态变更),我们确立 **C. 混合双通道 + 泛型回调** 为设计基线。
### 1\. 接口定义基线 (C++14)
我们定义一个强类型的、支持 `TraceID` 注入的接口。
```cpp
class IEventBus {
public:
virtual ~IEventBus() = default;
/**
* @brief 订阅特定类型的事件
* @tparam EventType 事件结构体类型
* @param handler 回调函数,接收 const EventType&
*/
template <typename EventType>
void subscribe(std::function<void(const EventType&)> handler);
/**
* @brief 同步发布 (高优先级指令)
* @details 在当前线程立即执行所有订阅者。调用者会被阻塞直到所有处理完成。
* @param event 事件对象 (需继承自 BaseEvent 以携带 TraceID)
*/
template <typename EventType>
void publishSync(const EventType& event);
/**
* @brief 异步发布 (状态上报/非关键消息)
* @details 将事件放入无锁队列,由 EventBus 内部的 Worker 线程稍后处理。立即返回。
* @param event 事件对象
*/
template <typename EventType>
void publishAsync(const EventType& event);
};
```
### 2\. 核心实现机制
- **同步通道 (`publishSync`)**
- **实现**:直接查找 `std::unordered_map<std::type_index, std::vector<Handler>>`
- **锁策略**:使用 `std::shared_timed_mutex` (读写锁)。发布时加**读锁**(允许多个事件同时发布,只要不修改订阅关系),订阅时加**写锁**。
- **死锁规避****严禁**在 `publishSync` 的回调中再次调用 `subscribe`(修改订阅表)。允许递归调用 `publish`,但需注意栈溢出风险。
- **异步通道 (`publishAsync`)**
- **实现**:维护一个 `WorkQueue`。由于事件类型各异,队列元素需使用 `std::function<void()>` 包装器Type Erasure来存储“执行动作”而不是存储原始事件数据。
- **并发模型**
- **单分发线程 (默认)**:一个后台线程专门负责从队列取任务并执行。保证了同一事件的消费顺序。
- **队列选型****MoodyCamel `ConcurrentQueue`** (MPMC 无锁队列) 或 `boost::lockfree::queue`。鉴于麒麟系统库现状,若无第三方库,使用 `std::deque` + `std::mutex` + `std::condition_variable` 也是可接受的(吞吐量在控制面不是瓶颈)。
### 3\. TraceID 的隐式传递 (2.3.2 预埋)
- **基线要求**`IEventBus` 不仅仅是搬运工,它还是**上下文的管理者**。
- **同步发布时**`TraceID` 自然随着线程栈传递。
- **异步发布时**`publishAsync` 必须在**入队时**捕获当前线程的 `TraceID`,并将其打包到 Lambda 中。在**出队执行时**,先恢复该 `TraceID` 到线程本地存储 (TLS),再执行回调。
- *这解决了异步调用导致追踪链断裂的经典难题。*
### 4\. 异常安全边界
- **基线决策**`EventBus` **必须捕获**订阅者抛出的所有异常。
- **论证**一个模块的回调函数崩溃throw exception绝不能导致发消息的模块如调度器崩溃也不能中断后续其他模块接收该事件。
- **行为**`try-catch` 包裹每个 handler 的调用。捕获异常后,记录 `ERROR` 日志(包含 TraceID然后继续执行下一个 handler。
-----
## 总结与下一步行动
我们确立了 **2.3.1 事件总线** 的基线:
1. **架构****混合双通道 (Sync/Async)**。
2. **实现****泛型 Pub/Sub**,基于 C++14。
3. **安全****异常隔离** + **读写锁保护**
有了这个“神经系统”,我们可以开始讨论在这上面跑什么“数据包”了。为了让全链路追踪真正生效,我们需要定义事件的“信封”格式。
**下一步建议**:进入 **2.3.2 全链路追踪上下文传递 (Trace Context Propagation)**,详细定义 `BaseEvent` 结构和 `TraceContextGuard` 的实现机制。这是之前多次提到的 `TraceID` 落地的具体技术点。
**提问**:您是否确认 **“混合双通道 + 泛型回调”** 的事件总线基线?确认后我们将深入 2.3.2。

View File

@@ -0,0 +1,152 @@
---
tags: []
date created: 星期五, 十一月 21日 2025, 12:00:13 凌晨
date modified: 星期一, 十一月 24日 2025, 4:31:24 下午
---
# 2.3.2 全链路追踪上下文传递 (Trace Context Propagation)
## 一、 约束输入与对齐 (Constraints & Alignment)
基于设计文档和 C++14 环境,我们需要对齐以下硬性约束:
1. **无侵入性 (Non-Intrusive)**:业务逻辑代码(如算法计算)不应到处传递 `trace_id` 参数。追踪上下文的获取应当是“隐式”的。
2. **跨线程连续性 (Cross-Thread Continuity)**:系统大量使用异步队列(`EventBus::publishAsync`和工作线程池。TraceID 必须能跨越线程边界,不能断链。
3. **性能极其敏感**:追踪机制是**热路径 (Hot Path)**。获取当前 TraceID 的开销必须是纳秒级,严禁涉及锁竞争或复杂的哈希查找。
4. **来源明确**
- **数据面**:由 `DataReceiver` 在收到 UDP 包时生成。
- **控制面**:由 `TaskScheduler` 在定时任务或外部 API 调用时生成。
---
## 二、 权衡分析与选项呈现 (Trade-off Matrix)
### 议题 1上下文存储方式 (Storage Mechanism)
|**选项**|**A. 显式参数传递 (Explicit Parameter)**|**B. 全局 Map 映射 (Global Map)**|**C. 线程本地存储 (Thread Local Storage - TLS) (推荐)**|
|---|---|---|---|
|**机制**|每个函数增加 `const TraceId& tid` 参数。|维护 `Map<ThreadID, TraceID>`。|使用 C++ `thread_local` 关键字。|
|**侵入性**|**极高**。所有接口签名都要改,污染业务代码。|**低**。但在读写时需要加锁(或无锁 Map有性能开销。|**零**。业务代码无感。|
|**性能**|最佳。|差(锁竞争)。|**极佳**。直接的内存地址访问,无锁。|
|**缺陷**|代码丑陋。|性能瓶颈。|**跨线程时会丢失**(需额外机制弥补)。|
### 议题 2跨线程传递策略 (Propagation Strategy)
针对 TLS 跨线程丢失的问题:
|**选项**|**A. 手动拷贝 (Manual Copy)**|**B. 智能闭包捕获 (Smart Closure Capture) (推荐)**|
|---|---|---|
|**机制**|在 `publishAsync` 前手动取出 ID在回调里手动设置。|封装 `EventBus` 的任务包装器,**在入队瞬间自动捕获 TLS在执行瞬间自动恢复 TLS**。|
|**可靠性**|**低**。开发者容易忘,导致断链。|**高**。由基础设施层保证,业务无感。|
|**复杂度**|低。|中。需要编写通用的任务包装模板。|
---
## 三、 基线确立与实施规范
为了实现“高性能”与“全链路无感”,我们确立 **C. 线程本地存储 (TLS) + B. 智能闭包捕获** 为技术基线。
### 1. 核心数据结构基线
- **`TraceId` 类型**:使用 `uint64_t``uuid`(推荐 64 位整数配合 SnowFlake 算法,追求极致性能)。
- **`BaseEvent` 接口**:所有控制面事件必须继承此基类。
```cpp
struct BaseEvent {
uint64_t trace_id; // 事件携带的“信封”
uint64_t timestamp;
BaseEvent() {
// 构造时自动从当前线程 TLS 捕获 TraceID
// 如果当前是根源(无 ID则保持 0 或生成新 ID视策略而定
trace_id = TraceContext::getCurrentId();
timestamp = CurrentTimeMillis();
}
};
```
### 2. 上下文管理基线 (RAII + TLS)
我们定义一个静态辅助类 `TraceContext` 和一个 RAII 守卫 `TraceContextGuard`。
- **`TraceContext` (TLS 管理)**
```cpp
class TraceContext {
public:
static void set(uint64_t id) { current_trace_id_ = id; }
static uint64_t get() { return current_trace_id_; }
static void clear() { current_trace_id_ = 0; }
// 生成一个新的全局唯一 ID
static uint64_t generateNew();
private:
// 核心:每个线程独立一份,无锁,极速
static thread_local uint64_t current_trace_id_;
};
```
- **`TraceContextGuard` (RAII 自动恢复)**
- **作用**:在作用域结束时自动还原之前的 ID支持嵌套调用。
- **场景**:用于事件处理函数入口,确保处理完事件后,线程状态复原,不污染后续逻辑。
### 3. EventBus 集成规范 (跨线程核心)
这是本节最关键的设计:**如何在 `publishAsync` 时“偷渡”上下文?**
- **基线实现逻辑**
1. **Publish 时 (线程 A)**`publishAsync` 函数内部,获取当前线程 A 的 `TraceContext::get()`。
2. **入队时**:将取出的 `trace_id` 和用户的 `handler` 打包成一个 `WrappedTask`。
3. **Execute 时 (线程 B)**`WrappedTask` 被执行。它首先使用 `TraceContextGuard` 将线程 B 的 TLS 设置为保存的 `trace_id`,然后执行用户 `handler`,最后 RAII 自动清理。
- **伪代码范式**
```cpp
template <typename EventType>
void IEventBus::publishAsync(const EventType& event) {
// 1. 捕获上下文 (此时还在发送者线程)
uint64_t context_id = event.trace_id;
// 2. 包装任务 (Lambda Capture)
auto wrapped_task = [handler, event, context_id]() {
// 3. 恢复上下文 (此时已在接收者线程)
TraceContextGuard guard(context_id);
// 4. 执行业务逻辑 (此时日志库能读到正确的 TLS TraceID)
handler(event);
};
// 5. 推入队列
work_queue_.push(wrapped_task);
}
```
### 4. 日志集成规范
- **基线要求**:所有日志宏(如 `RADAR_INFO`)必须自动读取 `TraceContext::get()` 并打印。
- **格式**`[Time][Level][ThreadID][TraceID] Message`。
- **效果**:业务代码只需写 `RADAR_INFO("Processing data")`,日志文件里会自动出现 `… [TraceID: 12345] Processing data`。
---
## 总结2.3.2 基线图谱
|**维度**|**基线决策**|**关键技术点**|
|---|---|---|
|**存储**|**Thread Local Storage (TLS)**|`thread_local uint64_t`,无锁,纳秒级访问。|
|**载体**|**`BaseEvent` 继承**|所有事件自动携带 `trace_id` 字段。|
|**跨线程**|**智能闭包捕获 (Smart Capture)**|`EventBus` 在入队/出队时自动 Switch Context。|
|**作用域**|**RAII Guard**|`TraceContextGuard` 保证作用域内的上下文一致性和退出后的还原。|
---
**下一步行动**
我们已经搞定了“大喇叭总线”和“条形码TraceID”。接下来要定的是“握手暗号” —— **2.3.3 生命周期编排与状态同步协议**。
在这个系统中,模块启动不是简单的 `start()`,它涉及依赖检查、顺序控制和状态回执。比如,`SignalProcessor` 启动前必须确认 GPU 资源就绪,启动后必须告诉调度器“我好了”。
**提问**:您是否确认 **“TLS + 智能闭包捕获”** 的上下文传递基线?确认后我们将进入 2.3.3。

View File

@@ -0,0 +1,102 @@
---
tags: []
date created: 星期五, 十一月 21日 2025, 2:27:11 下午
date modified: 星期五, 十一月 21日 2025, 2:52:14 下午
---
# 2.3.3 生命周期编排与状态同步协议 (Lifecycle Orchestration & State Synchronization)
遵循三阶段模型,我们深入探讨 **2.3.3 生命周期编排与状态同步协议 (Lifecycle Orchestration & State Synchronization)**
这是控制平面的核心业务流程。如果说事件总线是“电话线”,那么本节我们要规定的是“通话规矩”:调度器(指挥官)如何下达开机命令,模块(士兵)如何反馈执行结果,以及如何确保全员步调一致。
## 一、 约束输入与对齐 (Constraints & Alignment)
基于 `05_任务调度器设计.md` 和前序基线,我们面临以下硬性约束:
1. **决策权集中**`TaskScheduler` 是唯一的生命周期决策者。模块严禁擅自启动或停止,必须响应调度器的指令。
2. **异步闭环**:由于模块的初始化(如 GPU 上下文创建、网络绑定)可能耗时较长(> 10ms**严禁**在事件回调中阻塞执行。协议必须是 **“异步指令 -> 后台执行 -> 异步回执”** 的闭环模式。
3. **依赖有序**:启动必须遵循 `DependencyGraph` 的拓扑正序,停止遵循逆序。
4. **可观测性**:所有生命周期事件必须携带 `TraceID`,以便追踪“是谁触发了这次启动”。
---
## 二、 权衡分析与选项呈现 (Trade-off Matrix)
### 议题 1指令交互模式 (Command Interaction Model)
|**选项**|**A. 同步调用 (Direct Method Call)**|**B. 异步事件 + 超时机制 (Async Event + Timeout) (推荐)**|
|---|---|---|
|**机制**|调度器直接调用 `module->start()`。|调度器发布 `StartModuleEvent`,启动定时器,等待 `ModuleRunningEvent`。|
|**阻塞性**|**高**。如果模块 `start()` 卡死,调度器也会卡死,导致整个控制面瘫痪。|**无**。调度器发完指令就去处理别的(如响应心跳),不会被卡住。|
|**超时处理**|困难。需要多线程强杀。|**简单**。定时器触发后,如果没收到回执,直接判定启动失败并回滚。|
|**适用场景**|简单的函数库调用。|**分布式/微服务架构的标准解**(即使是进程内)。|
### 议题 2状态同步与一致性 (State Consistency)
|**选项**|**A. 乐观信任 (Trust Event)**|**B. 双重确认 (Event + Query) (推荐)**|
|---|---|---|
|**机制**|调度器只根据收到的 `ModuleRunningEvent` 更新内部状态表。|调度器收到 Event 更新状态,**同时**定期(如每 1 秒)调用 `module->getState()` 核对。|
|**风险**|**状态漂移**。如果 Event 丢失(极少见但可能),调度器会以为模块还在运行,实际上它可能已崩溃。|**健壮**。能自动修复“幽灵状态”,确保监控视图的真实性。|
|**开销**|零。|低(原子变量读取)。|
---
## 三、 基线确立与实施规范
为了确保系统在无人值守环境下的绝对可靠性,我们确立 **B. 异步事件 + 超时机制****B. 双重确认** 为基线。
### 1. 核心事件定义基线
所有事件必须继承自 2.3.2 确立的 `BaseEvent` 以携带 `TraceID`
- **指令事件 (Commands)** - 由调度器发布,模块订阅:
- `StartModuleEvent { string module_name; Config config_patch; }`
- `StopModuleEvent { string module_name; bool force; }`
- `PauseModuleEvent { string module_name; }`
- **回执事件 (Feedbacks)** - 由模块发布,调度器订阅:
- `ModuleRunningEvent { string module_name; }`
- `ModuleStoppedEvent { string module_name; }`
- `ModuleFailedEvent { string module_name; ErrorCode code; }`
### 2. 握手协议时序基线 (Sequence Flow)
这是“启动一个模块”的标准操作流程SOP
1. **指令下发**:调度器发布 `StartModuleEvent(Target="SignalProcessor")`,并将模块状态标记为 `STARTING`。同时,**启动一个 5 秒(可配置)的看门狗定时器**。
2. **异步执行**`SignalProcessor` 收到事件,**不应在回调中直接干活**,而是将“启动任务”提交给自己的工作线程(或 `std::thread`),立即返回。这保证了调度器线程不被阻塞。
3. **任务执行**`SignalProcessor` 的工作线程执行 `cudaFree(0)`、分配内存池等耗时操作。
4. **回执上报**
- **成功**:发布 `ModuleRunningEvent`
- **失败**:发布 `ModuleFailedEvent`
5. **闭环确认**
- **正常**:调度器收到 `ModuleRunningEvent`,取消定时器,将状态标记为 `RUNNING`,并触发下一个依赖模块的启动。
- **超时**:定时器先触发。调度器判定启动失败,发布 `StopModuleEvent(force=true)` 进行清理,并进入故障恢复流程。
### 3. 状态机一致性基线
- **双重账本**
- **账本 A (调度器侧)**`ModuleRegistry` 中的状态表,用于决策。
- **账本 B (模块侧)**:模块内部的 `std::atomic<State>`,用于执行。
- **同步规则**
- **写操作**:必须通过“指令 - 回执”流程修改。
- **读操作**:调度器每秒执行一次 `SystemHealthCheck`,对比 账本 A 和 账本 B。如果发现不一致如调度器认为 `RUNNING` 但模块是 `STOPPED`),触发 `StateMismatchEvent` 告警,并以**模块侧(真实世界)** 为准进行状态修正Self-Healing
---
## 总结与下一步行动
我们确立了 **2.3.3 生命周期编排与状态同步协议** 的基线:
1. **协议****全异步 + 超时看门狗**。
2. **一致性****事件驱动更新 + 定期主动核对**。
3. **依赖**:严格遵循 DAG 拓扑序。
下一步建议:
模块启动之后,难免会遇到运行时错误。这就涉及到 2.3.4 故障传播与恢复信令 (Fault Propagation & Recovery Signaling)。我们需要定义:当一个模块“挂了”的时候,它怎么“优雅地”告诉调度器?调度器又如何指挥其他模块进行“无感恢复”?
**提问**:您是否确认 **“异步指令 + 超时闭环”** 的生命周期协议基线?确认后我们将深入 2.3.4。

View File

@@ -0,0 +1,122 @@
---
tags: []
date created: 星期五, 十一月 21日 2025, 3:14:02 下午
date modified: 星期一, 十一月 24日 2025, 4:31:32 下午
---
# 2.3.4 故障传播与恢复信令 (Fault Propagation & Recovery Signaling)
这是系统的“求救与自愈”机制。在 2.3.3 中,我们定义了正常的生老病死;而 2.3.4 则要定义当模块“意外暴毙”时,系统如何避免全面崩溃,并有序地重新站起来。
特别需要注意的是,根据您的 **附件:生产环境架构加固指南**,我们引入了父进程 Watchdog。因此本节的故障处理必须划清界限**逻辑故障归调度器管,进程崩溃归 Watchdog 管**。
## 一、 约束输入与对齐 (Constraints & Alignment)
基于设计文档,我们需对齐以下硬性约束:
1. **职责边界**
- **模块职责**:严禁在内部无限重试致命错误(如 GPU ECC 错误),必须立即停止并上报。
- **调度器职责**:作为唯一决策者,负责编排恢复流程。
- **Watchdog 职责**:仅在整个进程 Segfault 或调度器死锁时介入。
2. **依赖感知**:恢复不能只重启故障模块,必须先暂停其上游的数据流,防止积压导致 OOM内存溢出
3. **防风暴**:必须集成熔断机制,防止一个持续故障的模块引发系统无限重启循环。
---
## 二、 权衡分析与选项呈现 (Trade-off Matrix)
### 议题 1故障上报内容的标准化 (Fault Reporting Standard)
|**选项**|**A. 仅错误码 (Error Code Only)**|**B. 丰富上下文 (Rich Context) (推荐)**|
|---|---|---|
|**机制**|`ModuleFailedEvent { int code; }`|`ModuleFailedEvent { ErrorCode code; string reason; Snapshot state; TraceID trace; }`|
|**诊断能力**|**低**。日志里只能看到 "Error 1001",无法知道当时 GPU 显存剩多少,或者正在处理哪一帧。|**高**。携带了案发现场的快照和 TraceID能直接关联到导致崩溃的那条控制指令。|
|**传输开销**|极低。|低(拷贝几个字符串和结构体)。|
|**决策支持**|弱。调度器只能无脑重启。|**强**。调度器可根据 `reason` 决定是立即重启,还是降级运行。|
### 议题 2恢复编排策略 (Recovery Orchestration)
|**选项**|**A. 激进重启 (Aggressive Restart)**|**B. 依赖感知流水线 (Dependency-Aware Pipeline) (推荐)**|
|---|---|---|
|**机制**|收到故障立即调用 `module->stop(); module->start();`。|`Pause Upstream` -> `Stop Faulty` -> `Start Faulty` -> `Resume Upstream`。|
|**数据安全性**|**低**。上游模块还在疯狂推数据,故障模块重启期间,中间队列瞬间爆满,导致丢包或内存溢出。|**高**。先截断水源,再修水管,修好再放水。|
|**复杂度**|低。|中。需要 `DependencyGraph` 支持。|
---
## 三、 基线确立与实施规范
为了实现“不仅能活过来,而且不丢数据”的智能恢复,我们确立 **B. 丰富上下文****B. 依赖感知流水线** 为基线。
### 1. 故障事件信封基线 (`ModuleFailedEvent`)
我们定义一个标准化的故障事件结构,作为所有模块的“遗言”。
```cpp
struct ModuleFailedEvent : public BaseEvent {
std::string module_name;
ErrorCode error_code;
bool is_hardware_fault; // 提示调度器:硬件坏了重启也没用,不如报警
std::string debug_info; // 包含显存状态、队列深度等现场快照
// 构造时自动捕获 TraceID
ModuleFailedEvent(string name, ErrorCode code, string info)
: module_name(name), error_code(code), debug_info(info) {
is_hardware_fault = IsHardwareError(code);
}
};
```
### 2. 恢复信令时序基线 (Recovery Signaling Sequence)
这是调度器收到 `ModuleFailedEvent` 后的标准**四步走**恢复协议。以 `SignalProcessor` (信号处理) 崩溃为例,其上游是 `DataReceiver` (数据接收)。
1. **止血 (Pause Upstream)**
- 调度器发布 `PauseDataFlowEvent(target="DataReceiver")`
- `DataReceiver` 响应:停止向中间队列 `push` 数据,暂时丢弃新数据或存入 RingBuffer并回复 `DataFlowPausedEvent`
2. **清理 (Stop Faulty)**
- 调度器发布 `StopModuleEvent(target="SignalProcessor", force=true)`
- `SignalProcessor` 响应:尝试释放 GPU 资源,清理线程。回复 `ModuleStoppedEvent`
3. **重启 (Restart Faulty)**
- 调度器发布 `StartModuleEvent(target="SignalProcessor")`
- `SignalProcessor` 响应:重新初始化 CUDA 上下文,申请内存。回复 `ModuleRunningEvent`
4. **恢复 (Resume Upstream)**
- 调度器发布 `ResumeDataFlowEvent(target="DataReceiver")`
- `DataReceiver` 响应:恢复向中间队列推送数据。系统恢复正常。
### 3. 熔断器集成基线 (Circuit Breaker)
为了防止“垂死挣扎”引发日志爆炸和 CPU 满载,必须在调度器侧实施熔断。
- **策略**:针对每个模块维护一个滑动窗口(如 60 秒)。
- **阈值**:若窗口内收到 **3 次** `ModuleFailedEvent`
- **动作**
- 标记该模块状态为 `BROKEN`
- 不再执行上述恢复流程。
- 发布 `SystemDegradedEvent`(系统降级)或 `SystemFailedEvent`(全系统停机)。
- 记录一条 `FATAL` 日志,呼叫运维人员。
---
## 总结与下一步行动
我们确立了 **2.3.4 故障传播与恢复信令** 的基线:
1. **协议****丰富上下文上报**(带 TraceID 和现场快照)。
2. **编排****依赖感知的四步恢复法**Pause -> Stop -> Restart -> Resume
3. **防护****三振出局**的熔断机制。
下一步建议:
系统能跑能修了,但在资源紧张(比如 GPU 满载)的时候,如何让重要任务(如显控界面渲染)优先运行?这就涉及到 2.3.5 资源仲裁与抢占式优先级控制。这是一个涉及“权力分配”的关键接口。
**提问**:您是否确认 **“依赖感知四步恢复 + 熔断器”** 的故障处理基线?确认后我们将深入 2.3.5。

View File

@@ -0,0 +1,130 @@
---
tags: []
date created: 星期五, 十一月 21日 2025, 3:15:00 下午
date modified: 星期一, 十一月 24日 2025, 4:31:51 下午
---
# 2.3.5 系统负载保护与热节流控制 (System Load Protection & Thermal Throttling)
## 一、 约束输入与对齐 (Constraints & Alignment)
基于变更后的架构目标,我们需对齐以下硬性约束:
1. **触发源单一性**:仅响应**物理传感器告警**(温度、功耗)。显控 UI 操作不再作为触发源。
2. **响应滞后性 (Hysteresis)**:热惯性是缓慢的。控制逻辑必须包含**迟滞Hysteresis**机制防止在阈值附近频繁切换导致系统震荡Flapping
3. **执行确定性**:节流动作必须是**确定性的降速**如每帧固定休眠而非不确定的丢包。丢包应由上游的背压机制Backpressure自然处理而不是由计算模块主动丢弃。
---
## 二、 权衡分析与选项呈现 (Trade-off Matrix)
### 议题 1降级执行策略 (Throttling Execution)
|**选项**|**A. 丢弃任务 (Task Dropping)**|**B. 动态降频 (DVFS / Clock Gating)**|**C. 软件占空比控制 (Software Duty Cycle) (推荐)**|
|---|---|---|---|
|**机制**|直接丢弃输入的 `DataContext`,不进行计算。|调用驱动 API (`ixsmi`) 强制降低 GPU 时钟频率。|在主处理循环中插入 `sleep()`,人为制造流水线气泡。|
|**热效应**|**极好**。GPU 完全空闲。|**好**。降低电压和频率。|**可控**。通过调整休眠时长精确控制负载率。|
|**副作用**|**数据断层**。破坏目标跟踪的连续性。|**依赖驱动**。智铠 SDK 可能未开放用户态调频接口,且响应慢。|**延迟增加**。但数据流保持连续,仅吞吐下降。|
|**适用性**|仅限 Level 3 (紧急停机)。|固件层保护(最后一道防线)。|**应用层首选**。通用、简单、不丢数据。|
### 议题 2控制回路设计 (Control Loop)
|**选项**|**A. 简单阈值 (Simple Threshold)**|**B. 迟滞比较器 (Hysteresis Comparator) (推荐)**|
|---|---|---|
|**机制**|>90°C 降速,<90°C 恢复。|>90°C 降速 (Trigger)<80°C 恢复 (Release)。|
|**稳定性**|****。会在 90°C 附近反复震荡导致风扇啸叫和电流波动。|****。确保系统冷却到安全区间后才恢复全速。|
---
## 三、 基线确立与实施规范
为了在极端工况下保护硬件同时维持最低限度的业务连续性我们确立 **C. 软件占空比控制** + **B. 迟滞比较器** 为基线
### 1. 交互协议基线 (Protocol)
定义一套闭环的保护协议
- **告警事件 (Alert)** - `MonitoringModule` 发布
- `SystemOverloadEvent { Type: THERMAL | POWER; Level: WARNING | CRITICAL; Value: float; }`
- _WARNING_: 接近阈值 85°C)。
- _CRITICAL_: 超过阈值 95°C)。
- **指令事件 (Command)** - `ResourceCoordinator` 发布
- `SetComputeThrottleEvent { ThrottleLevel level; TraceID trace_id; }`
### 2. 节流分级定义 (Throttle Levels)
|**等级**|**定义 (Level)**|**行为规范 (Behavior)**|**目标负载 (GPU Usage)**|**触发场景**|
|---|---|---|---|---|
|**L0**|**NO_THROTTLE**|全速运行无额外休眠。|100% (Max)|温度 < 80°C|
|**L1**|**LIGHT**|每帧处理后休眠 **5ms**。|~80%|80°C < 温度 < 90°C|
|**L2**|**HEAVY**|每帧处理后休眠 **20ms**。|~50%|温度 > 90°C|
|**L3**|**SUSPEND**|**暂停计算**。停止从队列取数据(触发背压)。|0% (Idle)|温度 > 95°C (濒临宕机)|
### 3. 迟滞控制逻辑 (Hysteresis Logic)
- **位置**`TaskScheduler` 中的 `ResourceCoordinator` 组件。
- **逻辑**
```cpp
// 伪代码:迟滞状态机
void onTemperatureUpdate(float temp) {
static State state = NORMAL;
switch (state) {
case NORMAL:
if (temp > 90.0) { state = THRORRLE_L2; publish(LEVEL_2); }
else if (temp > 85.0) { state = THRORRLE_L1; publish(LEVEL_1); }
break;
case THRORRLE_L1:
if (temp > 90.0) { state = THRORRLE_L2; publish(LEVEL_2); }
else if (temp < 75.0) { state = NORMAL; publish(LEVEL_0); } // 迟滞回落
break;
case THRORRLE_L2:
if (temp < 80.0) { state = THRORRLE_L1; publish(LEVEL_1); } // 迟滞回落
break;
}
}
```
### 4. 模块侧实现规范
- **组件**:在 `SignalProcessor` 中新增 `ThrottleController` 类。
- **侵入点**`ExecutionEngine` 的主循环末尾。
- **实现**
```cpp
void ExecutionEngine::run() {
while (running_) {
// 1. 获取数据 & 2. 提交计算 (…原逻辑…)
// 3. 节流控制点 (新增)
throttle_controller_.applyDelay();
}
}
void ThrottleController::applyDelay() {
// 读取原子变量,避免锁竞争
auto level = current_level_.load(std::memory_order_relaxed);
if (level == L1) std::this_thread::sleep_for(5ms);
else if (level == L2) std::this_thread::sleep_for(20ms);
else if (level == L3) std::this_thread::sleep_for(100ms); // 轮询等待
}
```
---
## 总结与下一步行动
我们确立了 **2.3.5 系统负载保护** 的基线:
1. **机制****软件级休眠 (Duty Cycle)**,牺牲延迟换取降温。
2. **策略****四级节流 + 迟滞控制**,防止系统震荡。
3. **触发**:仅限 **物理过载**
下一步建议:
控制面的“指挥”和“保护”都做好了。现在我们要解决最具挑战性的“动态调整”问题 —— 2.3.6 两阶段配置热更新协议。
比如,如何在雷达正在运转时,安全地把 CFAR 检测阈值从 15dB 改为 13dB或者切换波束扫描策略而不需要重启整个进程这需要一套严密的事务协议。
**提问**:您是否确认 **“四级节流 + 迟滞控制”** 的保护基线?确认后我们将深入 2.3.6。

View File

@@ -0,0 +1,138 @@
---
tags: []
date created: 星期五, 十一月 21日 2025, 3:40:21 下午
date modified: 星期一, 十一月 24日 2025, 4:32:10 下午
---
# 2.3.6 两阶段配置热更新协议 (Two-Phase Configuration Hot-Reload Protocol)
遵循三阶段模型,我们深入探讨 **2.3.6 两阶段配置热更新协议 (Two-Phase Configuration Hot-Reload Protocol)**
这是控制平面的“精细手术”机制。在雷达系统运行过程中,动态调整参数(如 CFAR 阈值、波束扫描范围)是一项高风险操作。如果配置变更导致某些模块崩溃,或者不同模块对同一参数的理解不一致,系统就会陷入“脑裂”或瘫痪。
因此,我们必须引入类似数据库事务的 **两阶段提交 (2PC)** 机制,确保配置更新要么**全员成功**,要么**全员回滚**。
## 一、 约束输入与对齐 (Constraints & Alignment)
基于系统的高可靠性要求,我们需对齐以下硬性约束:
1. **事务原子性 (Atomicity)**:配置变更必须是一个原子操作。严禁出现“模块 A 用了新参数,模块 B 还在用旧参数”的混合状态。
2. **非阻塞验证 (Non-blocking Validation)**:验证阶段不能阻塞业务数据流。模块应在后台检查新配置的合法性(如内存是否足够、参数是否越界)。
3. **无锁切换 (Lock-free Switch)**配置生效的瞬间Commit必须极其迅速不能让读取配置的业务线程`SignalProcessor` 的 Worker发生锁竞争。
---
## 二、 权衡分析与选项呈现 (Trade-off Matrix)
### 议题 1更新协议模型 (Update Protocol)
|**选项**|**A. 单阶段通知 (Fire-and-Forget)**|**B. 两阶段提交 (2PC / Prepare-Commit) (推荐)**|
|---|---|---|
|**机制**|`ConfigManager` 直接发布 `ConfigChangedEvent`。模块收到即生效。|**Phase 1**: `ValidateRequest` -> 收集投票。<br><br> <br><br>**Phase 2**: 全员通过 -> `CommitCommand`;否则 -> `Abort`。|
|**风险**|**高**。如果新配置导致模块 A 崩溃,或者模块 B 拒绝接受(如参数越界),系统将处于部分更新的**不一致状态**,且无法自动回滚。|**低**。所有模块都有机会在生效前“一票否决”。确保了变更的安全性。|
|**延迟**|低。|高(两轮 RTT。但在控制面秒级交互完全可接受。|
|**适用性**|日志级别调整等无关痛痒的配置。|**核心业务参数调整的标准解**。|
### 议题 2配置数据访问模型 (Access Model)
|**选项**|**A. 互斥锁保护 (Mutex Lock)**|**B. 双缓冲/原子指针 (Double Buffering / RCU) (推荐)**|
|---|---|---|
|**机制**|业务线程每次读配置前加锁:`lock(); val = config.val; unlock();`|业务线程持有 `shared_ptr<Config>`。更新时,`ConfigManager` 原子替换全局指针指向新对象。|
|**性能**|**差**。高频读取(如每秒 10k 数据包)会导致严重的锁竞争,甚至阻塞更新线程。|**极佳**。读取无锁(仅增加引用计数或直接解引用),写入原子替换。|
|**一致性**|强一致。|**最终一致**。旧线程继续用旧配置跑完当前帧,新线程用新配置。天然隔离了事务。|
---
## 三、 基线确立与实施规范
为了保障运行时变更的绝对安全,我们确立 **B. 两阶段提交** + **B. 原子指针 (RCU 风格)** 为基线。
### 1. 交互协议时序基线 (Sequence)
定义三个核心事件:
- `ValidateConfigEvent { ConfigID id; ConfigPatch patch; TraceID trace_id; }`
- `ConfigValidationResultEvent { ConfigID id; string module; bool success; string reason; }`
- `CommitConfigEvent { ConfigID id; }`
**时序流程**
1. **发起 (Prepare)**`ConfigManager` 收到 API 网关的变更请求,发布 `ValidateConfigEvent`
2. **投票 (Vote)**
- 各模块(`SignalProcessor`, `DataReceiver`)在后台校验新参数(例如:检查新 `threshold` 是否 > 0检查显存是否够用
- 模块发布 `ConfigValidationResultEvent` 反馈结果Yes/No
3. **决策 (Decide)**
- `ConfigManager` 收集回执。设定超时时间(如 1s
- **若全票通过**:发布 `CommitConfigEvent`
- **若有反对或超时**:记录错误日志,向 API 网关返回失败,流程终止(不发布 Commit系统保持原状
4. **提交 (Commit)**
- 模块收到 `CommitConfigEvent`,执行原子指针替换 (`std::atomic_store`),新配置即刻生效。
### 2. 模块侧实现规范 (C++14 RCU Pattern)
这是实现“无锁热更新”的关键代码范式。
- **配置持有**
```cpp
// 模块内部
std::shared_ptr<const ModuleConfig> current_config_;
std::mutex config_mutex_; // 仅用于保护指针的替换操作,不保护读取
```
- **业务读取 (Hot Path)**
```cpp
void processData() {
// 1. 获取当前配置的快照 (引用计数+1)
// 在这一帧处理期间即使外部更新了config_ptr 指向的内容也不会变,保证了单帧内的逻辑一致性
std::shared_ptr<const ModuleConfig> config_ptr = std::atomic_load(&current_config_);
// 2. 使用配置
float threshold = config_ptr->cfar_threshold;
// …
} // 3. config_ptr 析构,旧配置引用计数-1。如果归零自动释放内存。
```
- **更新提交 (Cold Path)**
```cpp
void onCommitConfig(const CommitConfigEvent& event) {
// 1. 构建新配置对象
auto new_config = std::make_shared<ModuleConfig>(*current_config_);
new_config->applyPatch(event.patch);
// 2. 原子替换
std::atomic_store(&current_config_, new_config);
// 3. 完成。旧配置对象会随着所有持有它的业务线程结束而被动释放。
}
```
### 3. 异常处理基线
- **超时机制**`ConfigManager` 在发起 `Validate` 后必须启动定时器。如果超时未收到所有模块的回执,视为**验证失败**。
- **版本对齐**:事件中必须携带 `ConfigID`(版本号或哈希)。模块在 Commit 时必须校验 ID 是否匹配,防止乱序到达的旧指令覆盖新指令。
---
## 总结与下一步行动
我们确立了 **2.3.6 两阶段配置热更新** 的基线:
1. **协议****投票 - 提交 (2PC)**,拒绝部分更新。
2. **实现****RCU (Read-Copy-Update)** 模式,读侧无锁,写侧原子替换。
3. **安全****超时自动回滚**。
下一步建议:
至此,控制指令的通道全部打通。最后,我们需要关注“只读”的数据流 —— 2.3.7 性能指标遥测通道。
业务模块如何高频地(如每秒 100 次上报性能指标FPS、吞吐量而完全不影响主业务线程的性能
**提问**:您是否确认 **“2PC + RCU 无锁替换”** 的热更新基线?确认后我们将完成 2.3 节的最后一个议题 2.3.7。

View File

@@ -0,0 +1,134 @@
---
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` 缓存,无锁热路径,有损发送|