创建仓库
This commit is contained in:
@@ -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(多生产单消费)队列** 的高吞吐、无锁上报策略,彻底消除监控逻辑对业务主线程的锁竞争干扰。
|
||||
@@ -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。
|
||||
152
系统基座文件/2/2.3/2.3.2 全链路追踪上下文传递 (Trace Context Propagation).md
Normal file
152
系统基座文件/2/2.3/2.3.2 全链路追踪上下文传递 (Trace Context Propagation).md
Normal 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。
|
||||
@@ -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。
|
||||
@@ -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。
|
||||
@@ -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。
|
||||
@@ -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(¤t_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(¤t_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。
|
||||
134
系统基座文件/2/2.3/2.3.7 性能指标遥测通道 (Performance Telemetry Channel).md
Normal file
134
系统基座文件/2/2.3/2.3.7 性能指标遥测通道 (Performance Telemetry Channel).md
Normal 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` 缓存,无锁热路径,有损发送|
|
||||
Reference in New Issue
Block a user