# Web_AutoNotificatPhone_Server_Prod ## 1. 项目概述 这是一个基于 **ASP.NET Core 8 MVC** 的通知与巡检服务,核心职责是: 1. 提供 HTTP API,接收“电话/短信”通知请求并发布到 Redis 频道。 2. 运行后台定时任务(`TimerClass`),每分钟做系统状态检查。 3. 根据 Redis 指标和 PostgreSQL 心跳数据触发自动告警(短信 + 电话)。 项目本质上是一个“**告警编排层**”: - 输入来源:HTTP 请求、Redis 指标、PostgreSQL 心跳表。 - 输出目标:Redis 发布(供下游呼叫/短信系统消费)。 --- ## 2. 技术栈与依赖 - .NET: `net8.0` - Web: ASP.NET Core MVC + API Controller - Redis: `CSRedisCore` - PostgreSQL: `Npgsql` - Logging: `NLog` `AutoNotificatPhone.csproj` 关键包: - `CSRedisCore` - `NLog` - `Npgsql` --- ## 3. 目录与核心文件 - `Program.cs`:应用启动、CORS、路由、HostedService 注册。 - `Controllers/CallAndMsgController.cs`:通知入站 API(电话/短信)。 - `Models/TimerClass.cs`:后台巡检与告警主流程。 - `CSRedisCacheHelper.cs`:Redis 连接与基础操作封装。 - `Models/SmsRequest.cs`:通知请求模型。 - `Models/ReturnInfo.cs`:统一接口返回结构。 - `appsettings.json`:日志级别、Postgres 连接配置。 - `nlog.config`:日志落盘规则。 --- ## 4. 启动流程(程序生命周期) ### 4.1 Program 启动阶段 `Program.cs` 主要行为: 1. `AddControllersWithViews()`:注册 MVC 和 API 支持。 2. 注册 CORS 策略 `AllowAnyOrigin`: - 允许任意源 - 允许任意方法 - 允许任意请求头 3. `AddHostedService()`:注册后台巡检服务。 4. 管道顺序: - `UseStaticFiles()` - `UseRouting()` - `UseCors("AllowAnyOrigin")` - `UseAuthorization()` - `MapControllerRoute(...)` ### 4.2 后台服务启动 应用启动后,`TimerClass` 自动运行: - 持续循环,直到应用停止。 - 每分钟固定在第 30 秒执行一次巡检逻辑。 --- ## 5. API 处理流程(CallAndMsgController) ## 5.1 路由 Controller 路由前缀: - `api/CallAndMsg/{action}` 核心接口: - `POST api/CallAndMsg/SendToPhone` ## 5.2 请求模型(SmsRequest) - `PhoneNumber`:目标手机号 - `CallerName`:任务标识/来电显示名 - `Content`:消息内容 - `StartingPoint`:开始时间(Unix 秒) - `DeadLine`:截止时间(Unix 秒) - `Type`:`1` 电话,`2` 短信 ## 5.3 接口执行逻辑 `SendToPhone` 内部流程: 1. 接收 `SmsRequest`。 2. 组装 `notification` 对象。 3. 使用固定频道 `MsgAndCall` 发布到 Redis: - `CSRedisCacheHelper.Publish("MsgAndCall", payload)` 4. 返回 `ReturnInfo`: - 成功:`status=200` + 根据 `Type` 返回“已加入队列”文案 - 失败:`status=500` + 异常信息 > 说明:`ConvertTimestampToIso` 名称为“转 ISO”,但实际仍返回 Unix 秒。 --- ## 6. Redis 封装(CSRedisCacheHelper) ## 6.1 连接 静态初始化时连接本地 Redis: - Host: `127.0.0.1` - Port: `10079` - 密码:代码中硬编码 - DB0: `redis` - DB1: `redis1` ## 6.2 用途 - `DB0`:主要用于发布通知频道(`MsgAndCall`)。 - `DB1`:主要用于读取巡检指标(CPU、在线数、收发包等)。 ## 6.3 方法 - `Set/Get/Forever/Del/Contains` - `Publish` --- ## 7. 核心:TimerClass 业务流程详解 `TimerClass` 是项目核心,分为 **调度层**、**检查层**、**告警层**。 ## 7.1 调度层(每分钟触发) 主循环执行顺序: 1. 计算下次执行时间(每分钟第 30 秒)。 2. 检查电话机进程是否在线(进程名 `Telephone`)。 3. 运行整点任务调度。 4. 依次执行检查任务: - CPU 阈值 - RCU 在线数量 - 总发送包 - 总接收包 - PostgreSQL 心跳 出现异常时: - 记录日志 - 延迟 10 秒重试 ## 7.2 整点任务调度 方法:`RunHourlyNotificationTask()` 规则: - 使用北京时间(UTC+8)。 - 非整点(`Minute != 0`)不执行。 - 同一整点只执行一次(`_executedTasks` 去重)。 - 10/15/22 点执行每日任务,其余整点发整点短信。 ### 7.2.1 整点短信(SendHourlySms) - 给 `Mobile1` 发送一条短信任务。 - 文案包含当前“月/日/时”。 ### 7.2.2 每日任务(ExecuteDailyTask) - 给 `Mobile1` 和 `Mobile2` 发送短信。 - 给 `Mobile1` 发送电话任务。 - 任意发送失败记录错误日志。 ## 7.3 Redis 指标检查 ### 7.3.1 CPU 检查(CheckCpuThreshold) 步骤: 1. 读取 `UDPPackage_DetectTime`: - 若超过 10 分钟未更新,触发“监控程序无法访问”告警。 2. 读取: - `UDPPackage_CPUMax` - `UDPPackage_CPUAvg` - `UDPPackage_CPUMin` 3. 若 `CPUAvg` 中满足“>=80 的点数达到 6 个”,触发 CPU 告警。 ### 7.3.2 通用 Redis 规则(CheckRedisValue) 用于在线数/发送包/接收包的基线跌落判断: 1. 读取 CSV 序列。 2. 取前 `baselineCount` 个点做平均。 3. 阈值 = 平均值 × `thresholdRatio`。 4. 若后续两个点都低于阈值,触发告警。 具体调用: - `RCUOnLine`:`baselineCount=8`,`ratio=0.8` - `UDPPackage_TotalSendPackage`:`baselineCount=8`,`ratio=0.6` - `UDPPackage_TotalRecvPackage`:`baselineCount=8`,`ratio=0.75` ### 7.3.3 接收包特殊规则(CheckTotalRecvPackage) 在通用规则之前,先判断: - 若最后 3 个值都 `< 70000`,立即触发告警。 ## 7.4 PostgreSQL 心跳检查 方法:`CheckKafkaHeartbeatAsync()` 目标:判断“Kafka 入库是否活跃”。 流程: 1. 从 `appsettings.json` 的 `Postgres` 节生成连接串。 2. 查询 `heartbeat.heartbeat_events`: - 先按 `ts_ms desc` 取最近 3000 条 - 再按 `write_ts_ms desc` 取最新一条 3. 结果为空或解析失败:走“数据库连接失败”告警路径。 4. 若 `nowMs - lastTsMs > KafkaStaleMinutes`:触发“入库停滞”告警。 5. 异常(连接失败/SQL 异常)同样走“数据库连接失败”告警。 ### 7.4.1 数据库连接失败节流 - 字段:`_kafkaDbConnectionAlertCount` - 逻辑:失败累计到 `KafkaDbAlertTriggerCount=8` 才真正发一次告警,之后计数清零。 --- ## 8. 告警发送机制 统一通过 `SendAlert(...)`: 1. 计算过期时间: - 普通:短信 1800 秒、电话 900 秒 - 扩展:短信 3600 秒、电话 1800 秒 2. 构造 3 条任务: - 短信:`Mobile1` - 短信:`Mobile2` - 电话:`Mobile1` 3. 调用 `CallAndMsgController.SendToPhone` 投递。 4. 任一失败,记录错误日志。 --- ## 9. 配置说明 ## 9.1 appsettings.json 当前包含: - `Logging` - `AllowedHosts` - `Postgres` - `Host` - `Port` - `Database` - `User` - `Password` - `MaxConnections` - `IdleTimeoutMs` ## 9.2 nlog.config - 文件落盘目录:`{basedir}/Logs/{shortdate}` - 文件:`info_*.txt`、`error_*.txt` - 当前规则: - `FATAL` 写入 info 文件 - `Error` 及以上写入 error 文件 --- ## 10. 快速运行 ## 10.1 开发运行 ```bash dotnet restore dotnet build AutoNotificatPhone.csproj dotnet run --project AutoNotificatPhone.csproj ``` 默认开发地址见 `Properties/launchSettings.json`: - `http://localhost:5080` ## 10.2 VS Code Tasks 项目已配置任务: - `build` - `publish` - `watch` --- ## 11. 对外接口示例 请求: ```json POST /api/CallAndMsg/SendToPhone Content-Type: application/json { "PhoneNumber": "13500000000", "CallerName": "测试任务", "Content": "测试短信内容", "StartingPoint": 1760000000, "DeadLine": 1760001800, "Type": "2" } ``` 成功响应: ```json { "isok": true, "message": "短信已加入发送队列", "status": 200, "response": "success" } ``` --- ## 12. 关键数据流(一句话版) 外部请求/定时巡检 -> 生成 `SmsRequest` -> `CallAndMsgController` -> Redis 频道 `MsgAndCall` -> 下游系统消费执行。 --- ## 13. 当前实现中的注意点 1. `CallAndMsgController` 中 `ConvertTimestampToIso` 命名与实现含义不一致(返回 Unix 秒)。 2. `CSRedisCacheHelper` 中 Redis 密码硬编码在代码,不利于安全与环境切换。 3. `TimerClass` 的 `KafkaStaleMinutes` 与告警文案“3分钟内入库数据为0”存在语义不一致风险(建议统一)。 4. 当前 CORS 为完全放开,生产环境建议收敛来源。 --- ## 14. 后续可优化方向(可选) 1. 将手机号、阈值、执行小时、告警文案全部配置化。 2. 让 `TimerClass` 通过接口服务发送通知,避免直接 new Controller。 3. 增加单元测试: - Redis 阈值计算 - Kafka 心跳判定 - 告警节流逻辑 4. 增加健康检查端点(数据库/Redis 连通性)。 --- ## 15. 维护建议 每次修改巡检规则后,至少验证: 1. `dotnet build` 无编译错误。 2. Redis 有模拟数据时告警触发正确。 3. PostgreSQL 心跳超时/失败路径可复现。 4. `MsgAndCall` 频道消息格式未破坏下游兼容。