## Context Gateway Checker 当前是一个 Bun + React 全栈脚手架,仅包含 demo 验证逻辑(`/api/demo` 端点 + 前端展示连接状态)。项目已有完整的开发、构建、打包、测试链路。需要将其转化为一个可用的 HTTP 拨测工具。 现有基础设施: - Bun 后端:路由框架(`createFetchHandler`)、服务启动(`startServer`)、运行时配置解析(`readRuntimeConfig`) - React 前端:Vite + React + TypeScript,开发期通过 Vite proxy 转发 `/api/*` - 构建:Vite 前端构建 + Bun 单 executable 打包 - 测试:Bun test + smoke test ## Goals / Non-Goals **Goals:** - 提供完整的 HTTP 拨测能力:YAML 配置 → 定时并发拨测 → 结果持久化 → 可视化展示 - 支持灵活的拨测配置:per-target interval、自定义 method/header/body、expect 校验 - 前端 Dashboard 实时展示:总览统计、目标状态列表、历史记录、延迟趋势图 - 保持现有项目架构风格和构建打包链路 - 零外部运行时依赖新增(仅前端 recharts) **Non-Goals:** - 不做告警通知(邮件/短信/Webhook),仅 Dashboard 展示 - 不做数据自动清理/过期策略,保留全部历史记录 - 不做 SSE/WebSocket 实时推送,用轮询即可 - 不做拨测目标动态增删(需修改 YAML 后重启) - 不做认证/鉴权 - 不做分布式/集群部署 ## Decisions ### 1. 配置管理:YAML 统一配置 + 单 CLI 参数 **选择**:所有配置(server、数据目录、拨测默认值、目标列表)统一到 YAML 文件,CLI 只接受一个参数即配置文件路径。 **替代方案**: - CLI 参数 + 环境变量覆盖部分配置 → 配置分散,维护成本高 - TOML 格式 → Bun 无内置支持,需引入依赖 **理由**: - 用户明确要求"配置统一到 YAML 文件" - `Bun.YAML.parse()` 内置支持,零依赖 - 单参数 CLI 最简洁:`./gateway-checker ./probes.yaml` ### 2. 数据存储:SQLite(bun:sqlite) **选择**:使用 Bun 内置 `bun:sqlite` 模块,WAL 模式运行。 **替代方案**: - JSONL 文件追加 → 聚合查询需全表扫描,趋势计算复杂 - 外部 SQLite 库(better-sqlite3)→ bun:sqlite 已内置,无需引入 **理由**: - 趋势分析需要 `AVG(latency) GROUP BY hour` 等聚合查询,SQL 原生支持 - bun:sqlite 是 Bun 内置模块,不违反"不引入新依赖"约束 - WAL 模式支持并发读写 - 单 `.db` 文件,便于管理 ### 3. 调度模型:按 interval 分组 + 组内并发 **选择**:将所有 target 按其 interval 值分组,每组一个 `setInterval` timer,组内使用 `Promise.all` 并发拨测。 **替代方案**: - 全局统一 tick → 无法支持 per-target interval - 每个 target 独立 timer → 目标多时 timer 数量大,资源浪费 - 使用调度队列(如 BullMQ)→ 过度设计 **理由**: - 支持 per-target interval,满足不同服务不同频率的需求 - 相同 interval 的目标共享 timer,timer 数量 = 不同 interval 值的数量 - 组内并发保证批量效率,组间隔离互不影响 ### 4. 前端更新策略:轮询 **选择**:前端每 5-10 秒轮询 `/api/summary` 和 `/api/targets`。 **替代方案**: - SSE 服务端推送 → 实现复杂,拨测间隔 15-60s 级别无必要 - WebSocket → 更复杂,过度设计 **理由**: - 拨测间隔本身是 15-60s,5s 轮询延迟完全可接受 - 实现简单,无需维护长连接状态 - Dashboard 面板按需加载趋势数据(展开详情时请求) ### 5. 趋势图:recharts **选择**:引入 recharts 作为前端图表库。 **替代方案**: - 纯 SVG 手写 sparkline → 零依赖但代码量大,交互能力有限 - Chart.js → 非 React 原生,需要 wrapper - D3 → 过于底层 **理由**: - 用户确认允许引入轻量图表库 - recharts 是 React 原生图表库,与现有 React 技术栈一致 - 支持折线图、迷你 Sparkline,满足需求 - 社区活跃,文档完善 ### 6. 目标状态判定模型 **选择**:两层判定——`success`(请求是否完成)+ `matched`(是否符合 expect 规则)。 ``` ● UP = success ✓ && matched ✓ ● DOWN = !success || !matched ``` **理由**: - 区分"网络不可达"和"返回了非预期状态码"两种故障场景 - expect 规则可选,不配置时 matched 默认为 true - 前端可以根据 `success`/`matched` 分别展示不同故障原因 ### 7. 数据库 Schema 设计 **targets 表**:从 YAML 同步初始化,运行时只读。 **check_results 表**:只追加写入,索引 `(target_id, timestamp)` 加速历史查询。 **理由**: - targets 从 YAML 来,不提供运行时动态增删(符合 Non-Goals) - check_results 追加写入,无需更新/删除,简单可靠 - 按时间范围查询是最高频操作,复合索引覆盖 ## Risks / Trade-offs - **[YAML 格式错误导致启动失败]** → 解析时做完整校验,输出清晰错误信息(字段缺失、格式不对、值非法等),提前失败而非运行时出错 - **[并发拨测对目标服务器压力]** → 每组内 Promise.all 并发,但同一 group 的 tick 间隔内不会重复拨测。如果用户配置了大量目标且 interval 很短,可能对目标产生压力,这是用户配置责任 - **[SQLite 数据文件增长]** → 当前不清理,长期运行会增长。预留清理策略接口,后续可通过配置保留天数 - **[recharts 包体积]** → recharts gzip 后约 70KB,会增加前端 bundle 大小。对于内部工具可接受 - **[拨测请求超时阻塞]** → 使用 `AbortController` + `setTimeout` 实现超时,避免单个慢请求阻塞整组 - **[进程重启后丢失 timer 状态]** → 拨测是幂等的(无状态定时任务),重启后立即开始新一轮即可,无需恢复状态