sagarkapure217-sketch/api-monitoring

GitHub: sagarkapure217-sketch/api-monitoring

一个生产级 API 健康监控平台,通过后台队列执行周期性健康检查,自动检测停机事件并提供实时告警与可视化分析。

Stars: 0 | Forks: 0

# API 监控 API Monitor 是一个生产级、分布式的 API 和 Web 健康监控服务。它允许开发人员和运维团队监控目标 HTTP endpoint,跟踪响应延迟趋势,汇总健康指标,自动检测停机事件,并向监控所有者发送警报。 该项目的结构包括一个轻量级、容器化的 Express.js 后端,一个由 Redis 支持的 BullMQ 队列处理器,以及一个采用轻量级企业主题风格、简约现代的 React (Vite) 前端。 ## 架构概述 该平台采用解耦的后台 worker 架构,以确保健康检查不会影响 API 的响应速度或阻塞处理请求的线程。 ### 高层架构图 ``` graph TD User([User / Browser]) API[Express.js API Server] DB[(PostgreSQL Database)] RedisStore[(Redis Cache & Queue Store)] Worker[BullMQ Background Worker] Target[Target Web Server / API] User -->|HTTP Requests| API API -->|Write/Read Metadata| DB API -->|Enqueue Checks| RedisStore Worker -->|Fetch Repeatable Jobs| RedisStore Worker -->|Execute HTTP Check| Target Worker -->|Persist Check Result| DB Worker -->|Cache Status & Count Failures| RedisStore ``` ### 健康检查与事件检测流程 ``` sequenceDiagram autonumber actor TargetServer as Target HTTP Endpoint participant Worker as BullMQ Worker participant DB as PostgreSQL participant Redis as Redis Cache participant Alert as Alert Service (Resend) Note over Worker: Triggered by BullMQ Repeatable Job Worker->>TargetServer: GET request (with User-Agent & 10s Timeout) alt Response received (2xx - 5xx) TargetServer-->>Worker: HTTP Status Code & Latency else Connection Fail / Hangup / Timeout Note over Worker: Error caught (e.g. ETIMEDOUT, socket hang up) end Worker->>DB: INSERT check result (status 'UP' or 'DOWN') Worker->>Redis: Update Status Cache & Increment/Reset failure counter alt checkResult.status == 'DOWN' Worker->>DB: Query last 3 checks DB-->>Worker: Returns status list alt All 3 checks are 'DOWN' and no 'OPEN' incident exists Worker->>DB: INSERT into incidents (status = 'OPEN') Worker->>Alert: Trigger Down Alert email Alert-->>Alert: Send email to Monitor Owner end else checkResult.status == 'UP' Worker->>DB: UPDATE incidents SET status = 'RESOLVED' WHERE status = 'OPEN' alt 1 or more rows resolved Worker->>Alert: Trigger Recovery Alert email Alert-->>Alert: Send email to Monitor Owner end end ``` ## 技术栈 ### 后端 * **Node.js (>=20.0.0)**:核心 JavaScript runtime 环境。 * **Express.js**:用于 REST API 路由的轻量级框架。 * **PostgreSQL (v16)**:用于存储关系型元数据(用户、监控、检查和事件)的主数据库。 * **Redis (v7)**:作为队列的后端消息代理,并用于快速状态/故障缓存。 * **BullMQ**:基于 Redis 的队列库,用于调度和执行可重复的监控检查。 * **Axios**:用于执行出站 endpoint 检查的 HTTP 客户端。 * **Resend**:用于停机和恢复警报的事务性邮件 API 提供商。 ### 前端 * **React (v19)**:UI 组件库。 * **Vite**:前端构建工具和本地开发代理。 * **React Router (v6)**:客户端路由。 * **TanStack Query (React Query)**:有状态数据缓存、mutation 和自动 query 失效。 * **Recharts**:用于折线图和环形图的数据可视化库。 * **Tailwind CSS (v3)**:极简风格架构。 ## 数据库设计 ``` erDiagram USERS { uuid id PK text email UK text password_hash timestamptz created_at } MONITORS { uuid id PK uuid user_id FK text name text url integer interval_minutes boolean is_active timestamptz created_at } CHECKS { uuid id PK uuid monitor_id FK text status integer status_code integer response_time_ms text error_message timestamptz checked_at } INCIDENTS { uuid id PK uuid monitor_id FK text status timestamptz started_at timestamptz resolved_at } USERS ||--o{ MONITORS : "owns" MONITORS ||--o{ CHECKS : "has logs" MONITORS ||--o{ INCIDENTS : "triggers" ``` ### 表关系 * **`users`**:存储用户详细信息。电子邮件地址是唯一的。 * **`monitors`**:存储按周期监控的 HTTP 目标。每个监控都属于一个用户 (`user_id`)。删除用户时,监控会级联删除。 * **`checks`**:保存的健康检查日志。删除监控时级联删除。包含延迟、状态码和可选的网络错误日志。 * **`incidents`**:跟踪停机事件。当目标恢复时,状态会变更为 `RESOLVED`。 ## Redis 使用策略 在 Alexandria 中,Redis 有三个关键的基础设施用途: 1. **队列存储 (BullMQ)**:作为高吞吐量的队列代理。worker 会轮询 Redis 以获取可重复的检查任务。 2. **最新状态缓存 (Hash)**: * **Key**:`monitor:{monitorId}:status` * **字段**:`status`、`response_time_ms`、`checked_at`。 * **TTL**:`interval_minutes * 2 分钟`。防止在检查任务失败时提供过期的检查日志,同时减少仪表板页面刷新时的数据库读取流量。 3. **失败计数器**: * **Key**:`monitor:{monitorId}:fail_count` * **用途**:在连续失败运行时递增以监控稳定性。在检查恢复 (`UP`) 时立即删除。 * **TTL**:`600 秒`(10 分钟)。 ## 事件系统详情 ### 1. 开启事件 * 当出站请求导致 `DOWN` 状态(状态码 `>= 400` 或连接错误)时,在后台 worker 中触发。 * worker 会获取该监控的**最后 3 次检查**。 * 如果这**全部 3 次**检查均为 `DOWN` 状态,系统将检查该监控是否存在任何 `OPEN` 状态的事件。 * 如果不存在,则插入一条 `OPEN` 状态的事件记录,并通过 Resend 异步向监控所有者触发事务性停机警报邮件。 ### 2. 解决事件 * 当检查成功 (`UP`) 时在 worker 中触发。 * 它会执行一个原子 `UPDATE` 查询: UPDATE incidents SET status = 'RESOLVED', resolved_at = NOW() WHERE monitor_id = $1 AND status = 'OPEN'; * 如果恰好修改了 1 行,则会触发恢复警报邮件,详细说明停机持续时间和恢复时间戳。如果修改了 0 行,则会静默返回(幂等性保护)。 ## API 文档 所有监控 endpoint(公共健康路由除外)都需要 JWT Bearer token: `Authorization: Bearer ` ### 1. 公共 Endpoint #### `GET /health` * **认证**:无 * **描述**:数据库和 Redis 连接检查器。 * **成功响应 (200)**: { "status": "healthy", "database": "connected", "redis": "connected" } #### `POST /auth/register` * **认证**:无 * **请求体**: { "email": "user@example.com", "password": "strongpassword" } * **成功响应 (201)**: { "user": { "id": "e2926710-53bc-40d0-8f96-be9bf7491cf1", "email": "user@example.com", "created_at": "2026-06-08T18:00:00Z" } } * **错误响应 (409 Conflict)**: { "error": "Email is already registered" } #### `POST /auth/login` * **认证**:无 * **请求体**: { "email": "user@example.com", "password": "strongpassword" } * **成功响应 (200)**: { "user": { "id": "e2926710-53bc-40d0-8f96-be9bf7491cf1", "email": "user@example.com" }, "token": "eyJhbGciOiJIUzI1NiIsIn..." } * **错误响应 (401 Unauthorized)**: { "error": "Invalid email or password" } ### 2. 监控 API(需认证) #### `GET /monitors` * **描述**:获取经过身份验证的用户拥有的所有监控。 * **成功响应 (200)**: { "monitors": [ { "id": "d47b35a3-f173-4bfc-90c7-f34974f7861a", "user_id": "e2926710-53bc-40d0-8f96-be9bf7491cf1", "name": "Wikipedia", "url": "https://www.wikipedia.org/", "interval_minutes": 5, "is_active": true, "created_at": "2026-06-08T17:19:49.676Z" } ] } #### `POST /monitors` * **描述**:创建一个新的健康监控并调度其重复检查任务。 * **请求体**: { "name": "Wikipedia", "url": "https://www.wikipedia.org/", "interval_minutes": 5 } * **成功响应 (201)**: { "monitor": { "id": "d47b35a3-f173-4bfc-90c7-f34974f7861a", "user_id": "e2926710-53bc-40d0-8f96-be9bf7491cf1", "name": "Wikipedia", "url": "https://www.wikipedia.org/", "interval_minutes": 5, "is_active": true, "created_at": "2026-06-08T17:19:49.676Z" } } #### `PATCH /monitors/:id` * **描述**:编辑监控设置。*仅*公开允许的字段(`name`、`interval_minutes`、`is_active`)。如果间隔改变,会重新调度 worker 任务。 * **请求体**: { "name": "Wikipedia Main Portal", "is_active": false } * **成功响应 (200)**: { "monitor": { "id": "d47b35a3-f173-4bfc-90c7-f34974f7861a", "user_id": "e2926710-53bc-40d0-8f96-be9bf7491cf1", "name": "Wikipedia Main Portal", "url": "https://www.wikipedia.org/", "interval_minutes": 5, "is_active": false, "created_at": "2026-06-08T17:19:49.676Z" } } #### `DELETE /monitors/:id` * **描述**:删除监控,级联删除历史检查记录,并移除 BullMQ 检查任务。 * **成功响应 (200)**: { "message": "Monitor deleted successfully" } ### 3. 检查指标与历史记录(需认证) #### `GET /monitors/:id/status` * **描述**:获取最新的检查结果。优先查询 Redis 状态缓存。 * **成功响应 (200)**: { "check": { "status": "UP", "status_code": 200, "response_time_ms": 132, "error_message": null, "checked_at": "2026-06-08T18:17:43.000Z" } } #### `GET /monitors/:id/metrics` * **描述**:获取所有历史检查的条件聚合统计数据。 * **成功响应 (200)**: { "total_checks": 140, "up_checks": 139, "down_checks": 1, "uptime_percentage": 99.28, "avg_response_time_ms": 234.45 } #### `GET /monitors/:id/checks` * **描述**:获取目标最近 100 条原始检查日志。 * **成功响应 (200)**: { "checks": [ { "status": "UP", "status_code": 200, "response_time_ms": 240, "error_message": null, "checked_at": "2026-06-08T18:17:43.000Z" } ] } ## 安全性 * **密码哈希**:在注册和登录身份验证时,使用工作因子为 10 的 `bcrypt`。 * **身份验证**:完全基于 JWT 的 Bearer token。JWT token 会过期,需要重新登录刷新。 * **速率限制**: * **全局限制器**:每个 IP 地址每 15 分钟 100 次请求。 * **认证限制器**:在 `/auth/login` 和 `/auth/register` 上,每个 IP 每分钟 5 次请求,以阻止暴力破解尝试。 * **数据泄露防护**:作用域内的数据库查询通过 `WHERE user_id = $userId` 强制执行所有权验证。尝试访问或修改属于其他用户的监控将返回 `404 Not Found` 而不是 `403 Forbidden`,以防止账户枚举泄露。 ## 本地开发设置 ### 前置条件 * **Docker & Docker Compose** * **Node.js (>= 20.0.0)** ### 1. 后端服务启动 1. 克隆仓库并导航到项目目录: git clone cd api_monitoring 2. 在 `.env` 中配置环境变量: cp .env.example .env 3. 构建并启动容器套件: docker compose up --build -d 4. 在 API 容器内运行迁移以初始化 PostgreSQL schema: npm run migrate ### 2. 前端开发服务器 1. 导航到前端目录: cd frontend 2. 安装依赖: npm install 3. 启动 Vite 热重载开发服务器: npm run dev 4. 在浏览器中打开 [http://localhost:5173](http://localhost:5173)。代理会将所有 API 调用转发到后端的 `http://localhost:3000`。 ## 环境变量 | 变量 | 描述 | 示例 / 默认值 | |----------|-------------|-------------------| | `PORT` | REST API 服务器的监听端口 | `3000` | | `DB_HOST` | 关系型数据库主机指针 | `postgres`(或在 Docker 外为 `localhost`) | | `DB_PORT` | PostgreSQL 监听端口 | `5432` | | `DB_USER` | 数据库用户名凭据 | `api_user` | | `DB_PASSWORD`| 数据库用户密码 | `api_password` | | `DB_NAME` | 关系型数据库 schema 名称 | `api_monitoring` | | `REDIS_HOST` | Redis 缓存和队列代理主机 | `redis`(或在 Docker 外为 `localhost`) | | `REDIS_PORT` | Redis 服务器监听端口 | `6379` | | `JWT_SECRET` | 用于 token 验证的签名密钥 | `long_random_jwt_secret_phrase` | | `RESEND_API_KEY`| 性 Resend 邮件 token | `re_WuvHfLnV_4e...` | | `ALERT_FROM_EMAIL`| 已验证的 Resend 域名发件人 | `onboarding@resend.dev` | | `ALERT_TO_EMAIL`| 测试邮件覆盖地址 | `your_email@gmail.com` | ## 项目结构 ``` api_monitoring/ ├── migrations/ # SQL database schemas ├── scripts/ # Helper scripts (migrations launcher) ├── src/ │ ├── config/ # Redis, DB, and Env parsers │ ├── controllers/ # Request handlers (Auth, CRUD, Checks) │ ├── middleware/ # Rate limiters & JWT validators │ ├── queues/ # BullMQ connection adapters │ ├── routes/ # Express Router bindings │ ├── services/ # Relational operations & incident engines │ ├── workers/ # BullMQ checker process and DNS configuration │ ├── app.js # Express middleware chain │ └── server.js # HTTP listener and main bootstrap ├── frontend/ │ ├── src/ │ │ ├── api/ # Axios client configurations │ │ ├── components/ # Reusable layouts and Navbar │ │ ├── context/ # Auth session provider │ │ ├── pages/ # Views (Login, Register, Dashboard, Details) │ │ ├── index.css # Tailwind design tokens │ │ └── App.jsx # React Routing configuration │ ├── tailwind.config.js # Layout customization config │ └── vite.config.js # Proxied server routing ├── docker-compose.yml # Container configuration └── Dockerfile # API image configuration ``` ## 面试讨论要点 ### 为什么使用 BullMQ 和后台 Worker? * 如果检查在 Express 主线程中按计时器间隔运行,API 循环在负载下会变慢。将检查解耦到在单独的线程/容器中运行的 BullMQ worker,可以确保 API 保持响应。 * BullMQ 提供了稳健的任务保证,能够处理重试,并将执行状态存储在 Redis 中,从而防止任务丢失。 ### 为什么使用 Redis 缓存? * 否则,每次刷新仪表板概览页面都会导致 PostgreSQL 为每个监控获取最新检查。从 Redis hash 缓存中读取可以立即解析最新的检查状态,从而节省关系型数据库的 IOPS。 ### 为什么要有 3 次检查的失败缓冲? * 网络连接充满噪音。仅因一次请求超时就开启事件会导致“警报疲劳”。要求连续 3 次检查失败可确保警报的准确性,并能隔离瞬时的故障。 ## 未来改进 1. **重试抖动**:对检查目标实施指数重试退避,以避免对遭遇临时流量高峰的服务器造成 DDOS 攻击。 2. **死信队列 (DLQ)**:将永久失败的检查(例如目标主机 DNS 被删除)加入到 DLQ 中,而不是无限重试。 3. **WebSockets**:将最新的检查延迟从 worker 实时推送到前端仪表板,从而无需重新加载页面。
标签:API监控, API集成, BullMQ, Docker, GNU通用公共许可证, MITM代理, Node.js, React, Syscalls, 可观测性, 安全防御评估, 搜索引擎查询, 测试用例, 自定义脚本, 请求拦截, 运维监控