Files
pig-farm-controller/design/exceeding-threshold-alarm/index.md
2025-11-09 21:37:35 +08:00

138 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 需求
实现采集数据超过阈值报警
## issue
[实现采集数据超过阈值报警](http://git.huangwc.com/pig/pig-farm-controller/issues/62)
# 方案
1. **架构核心**: 新增一个 **告警领域服务**,作为告警系统的核心大脑,负责告警事件的生命周期管理。
2. **任务分离**:
* 新增 **阈值告警任务** (分为区域主控和普通设备两种),仅负责检测数据并将结果报告给领域服务。
* 新增 **告警通知发送任务**,作为一个独立的、系统预定义的定时任务,负责调用领域服务,获取并发送所有待处理的通知。
3. **计划调度**:
* 修改现有 "定时全量数据采集" 计划, 更名为 "周期性系统健康检查"。此计划包含固定的 **全量采集任务** 和由用户动态配置的
**阈值告警任务**
* 新增一个独立的 "告警通知发送" 计划,用于定时执行固定的 **告警通知发送任务**
4. **数据与接口**:
* 新增独立的告警记录表(建议采用“活跃告警表 + 历史告警超表”的模式)。
* 新增相应的告警配置管理接口。
## 方案细节
### 架构与职责划分
1. **告警领域服务 (`internal/domain/alarm/`) - 管理器**
* **职责**: 作为告警系统的核心大脑,负责处理告警事件的完整生命周期。
* **功能**:
* 接收来自检测任务的状态报告包含设备ID、传感器类型、当前是否异常等信息
* 根据报告和数据库中的告警记录,决策是创建新告警、更新为已解决、还是因被忽略而跳过。
* 管理“手动忽略” (`Ignored`) 状态和忽略到期时间 (`ignored_until`)。
* 实现可配置的“重复通知”策略(`re_notification_interval`),决定何时对持续存在的告警再次发送通知。
* 提供接口供 `告警通知发送任务` 调用,以获取所有待处理的通知。
2. **阈值告警任务 (`internal/domain/task/`) - 检测器**
* **职责**: 职责纯粹,仅负责执行检测并将结果报告给告警领域服务。
* **逻辑**: 从传感器数据表读取最新数据 -> 与自身配置的阈值进行比对 -> 无论结果如何,都调用 `告警领域服务.ReportStatus()`
报告当前状态(正常或异常)。
* **无状态**: 任务本身不关心告警是否已存在或被忽略,它只负责“状态同步”。
3. **告警通知发送任务 (`internal/domain/task/`) - 发送器**
* **职责**: 作为一个独立的定时任务,解耦通知发送与告警检测。
* **逻辑**: 调用 `告警领域服务.GetAndProcessPendingNotifications()` -> 获取待发送通知列表 -> 调用 `通知领域服务`
逐一发送。
* **优势**: 统一管理定时任务,实现资源控制,提高系统稳定性和可扩展性。
### 计划与任务调度
1. **"周期性系统健康检查" 计划**
* **任务构成**:
* **全量数据采集任务 (ExecutionOrder: 1)**: 系统预定义,必须是第一个执行的任务,为后续的告警检测提供最新的数据基础。
* **阈值告警任务 (ExecutionOrder: 2, 3...)**: 由用户通过API动态配置和管理`告警配置服务` 负责将其增删改到此计划中。
2. **"告警通知发送" 计划**
* **任务构成**: 包含一个系统预定义的 `告警通知发送任务`
* **调度**: 可配置独立的执行频率(如每分钟一次),与健康检查计划解耦。
3. **系统初始化 (`data_initializer.go`)**
* **职责**: 只负责创建和维护系统预定义的、固定的计划和任务。
* **操作**:
* 确保 "周期性系统健康检查" 计划存在,并包含 `全量数据采集任务`
* 确保 "告警通知发送" 计划存在,并包含 `告警通知发送任务`
* **注意**: 初始化逻辑 **不会****不应该** 触及用户动态配置的阈值告警任务。
### 阈值告警任务 (用户可配置的任务类型)
1. **任务类型**: 提供两种可供用户配置的阈值告警任务类型,分别对应 **区域主控****普通设备** 告警。
2. **参数结构**:
* **通用参数**: 任务参数将包含 `Thresholds` (阈值) 和 `Operator` (操作符,如 `>``<`) 字段。
* **普通设备任务**: 配置包含 `DeviceID`
* **区域主控任务**: 配置包含 `AreaControllerID`, `SensorType`, 以及一个 `ExcludeDeviceIDs` (需要排除的设备ID列表)。
### 告警事件与生命周期
1. **告警事件定义**:
* 区分 **告警规则** (配置的策略) 和 **告警事件** (规则被具体设备触发的实例)。
* 区域主控下不同设备触发的告警,即使基于同一规则,也应视为独立的 **告警事件**,以便于精确追溯和独立操作。
2. **生命周期管理**:
* **自动闭环**: 当阈值告警任务报告数据恢复正常时,告警领域服务会自动将对应的 `Active` 告警事件状态更新为 `Resolved`
* **手动忽略 (Snooze)**: 用户可通过接口将告警事件状态置为 `Ignored` 并设置 `ignored_until`
。在此期间,即使数据持续异常,也不会发送通知。忽略到期后若问题仍存在,告警将重新变为 `Active` 并发送通知。
* **持续告警与重复通知**: 对持续未解决的 `Active` 告警,只保留一条记录。告警领域服务会根据 `re_notification_interval`
配置的重复通知间隔,决定是否需要再次发送通知。
### 数据库设计考量
1. **冷热分离方案 (推荐)**:
* **`active_alarms` (活跃告警表)**:
* **类型**: 标准 PostgreSQL 表。
* **内容**: 只存放 `Active``Ignored` 状态的告警。
* **优势**: 保证高频读写的性能,避免在被压缩的数据上执行更新操作。
* **`historical_alarms` (历史告警表)**:
* **类型**: 改造为 **TimescaleDB 超表**
* **内容**: 存放 `Resolved` 状态的告警。当告警在 `active_alarms` 中被解决后,记录将移至此表。
* **优势**: 适合存储海量历史数据,便于分析、统计,并可利用 TimescaleDB 的压缩和数据生命周期管理功能。
2. **表结构字段**:
* `status`: 枚举类型,包含 `Active`, `Resolved`, `Ignored`
* `ignored_until`: `timestamp` 类型,记录忽略截止时间。
* `last_notified_at`: `timestamp` 类型,记录上次发送通知的时间。
### 阈值告警服务 (领域层)
1. **服务职责**:
* 负责管理阈值告警 **任务配置** 的增删改查。这些任务配置包含了具体的阈值规则。
* 负责将用户创建的阈值告警任务动态更新到 "周期性系统健康检查" 计划中。
* **任务配置引用检查**: 提供自检方法,用于在删除设备或设备模板前,检查它们是否被任何阈值告警任务配置所引用,以防止产生悬空引用。
2. **排除列表计算与联动**:
* **删除独立任务配置后归属**: 当一个普通设备的独立告警任务配置被删除时,它将自动从其所属区域主控的 `ExcludeDeviceIDs`
列表中移除,从而回归到区域统一告警策略的管理之下。
* **设备生命周期管理**: 在对设备进行修改(特别是更换区域主控)或删除时,以及在删除区域主控时,必须同步更新相关的
`ExcludeDeviceIDs` 列表,同时解决相关告警(当删除时), 以保证数据一致性。
* **实现**: `DeviceService` 中负责处理设备更新和删除的方法,需要调用本服务提供的“任务配置引用检查”和刷新接口。
### 阈值告警控制器
1. **独立接口**: 提供两组独立的 Web 接口,分别用于管理区域主控和普通设备的阈值告警配置。
* 区域主控告警配置接口: `/api/v1/alarm/region-config`
* 普通设备告警配置接口: `/api/v1/alarm/device-config`
2. **接口职责**: 接口负责接收前端请求,调用应用服务层的阈值告警服务来完成实际的业务逻辑。
### TODO
1. 是否要加一个延时操作, 因为采集是异步的, 采集任务结束时不一定能拿到最新数据, 所以需要一个延时操作等待区域主控上传
2. 统一一下区域主控的命名, 目前有AreaController和RegionalController, 不排除还有别的
3. 将数据类型转为float32, 节约空间, float64精度有些浪费, float32小数点后6-7位足够了
# 实现记录
1. 定义告警表和告警历史表
2. 重构部分枚举, 让models包不依赖其他项目中的包
3. 创建仓库层对象(不包含方法)
4. 实现告警发送任务
5. 实现告警通知发送计划/全量采集计划改名
6. 实现设备阈值检查任务