【应用】爬虫监控系统详细设计指南(附源代码)
1. 项目概述
本系统是一个 Web 爬虫监控平台,提供爬虫任务调度、数据采集记录管理、实时监控看板以及告警规则管理能力。系统由两部分组成:Next.js 前端控制台 和 Bun 爬虫调度微服务,通过 Caddy 反向代理统一对外暴露服务。
1.1 核心能力
- 多爬虫任务的创建、启停、触发与删除
- 爬取记录的浏览、预览、源码查看与下载
- 实时指标看板(请求量、成功率、响应时间、状态码分布、按小时/爬虫类型统计)
- 可配置的告警规则引擎(成功率、错误数、响应时间阈值)
- 爬虫调度器按指定频率自动执行爬取任务
- 种子数据自动初始化,开箱即用
1.2 技术栈
| 层级 | 技术选型 |
|---|---|
| 前端框架 | Next.js 16 (React 19, TypeScript) |
| UI 组件库 | shadcn/ui (Radix UI) |
| 样式方案 | Tailwind CSS 4 |
| 图表库 | Recharts |
| 前端状态管理 | React Hooks + Zustand |
| 后端微服务运行时 | Bun |
| ORM | Prisma 6 |
| 数据库 | SQLite (file-based) |
| 反向代理 | Caddy |
| 构建工具 | Bun (bundler) |
2. 系统架构
2.1 整体架构图
┌──────────────────────────────────────────────────────────────┐
│ Caddy (:81) │
│ Reverse Proxy │
│ ┌─────────────────────┬─────────────────────┐ │
│ │ │ │ │
│ ┌─────▼──────┐ ┌──────▼──────┐ │ │
│ │ Next.js │ │ Crawler │ │ │
│ │ Frontend │ │ Scheduler │ │ │
│ │ (Port │ │ Mini- │ │ │
│ │ 3000) │ │ Service │ │ │
│ │ │ │ (Port 3002)│ │ │
│ └─────┬──────┘ └──────┬──────┘ │ │
│ │ │ │ │
│ │ ┌──────────▼──────────┐ │ │
│ │ │ SQLite │ │ │
│ └────────► (custom.db) ◄─────────┘ │
│ └─────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
2.2 部署架构
系统采用 双进程 + 反向代理 模式部署:
- Next.js Standalone Server (
next-service-dist/server.js):服务前端页面与 API 路由,端口 3000 - Crawler Scheduler Mini-Service (
mini-services-dist/下的构建产物):爬虫调度引擎,端口 3002 - Caddy:监听 81 端口,根据
XTransformPort查询参数或默认路由转发请求 - SQLite 数据库:两进程共享同一数据库文件
3. 模块设计
3.1 前端模块
前端基于 Next.js App Router,使用单页面 Tab 架构组织功能。
3.1.1 页面路由
| 路径 | 组件 | 说明 |
|---|---|---|
/ |
page.tsx |
主入口,包含四个 Tab |
3.1.2 Tab 模块
| Tab | 组件 | 功能描述 |
|---|---|---|
| Dashboard | KPICards + HourlyChart + SpiderChart + StatusPieChart + RecentErrors |
KPI 指标卡片(总请求数、成功率、活跃任务数、平均响应时间),按小时请求趋势、按爬虫类型统计、HTTP 状态码分布、最近错误列表 |
| Tasks | TasksTab |
任务列表展示、创建/编辑/删除任务、启动/暂停、手动触发爬取 |
| Records | RecordsTab |
分页记录列表、按任务筛选、记录详情 Sheet(元数据 + HTML 预览 iframe + 源码查看/复制/下载/新标签页打开) |
| Monitoring | MonitoringTab |
日志子 Tab(按任务/级别筛选) + 告警子 Tab(告警列表、告警规则 CRUD、启用/禁用) |
3.1.3 自定义 Hook:useCrawlerAPI
封装所有前后端交互逻辑,提供统一的 API 调用接口和自动刷新机制:
- 自动刷新:每 10 秒轮询
/api/metrics、/api/tasks、/api/alerts、/api/alert-rules - 任务操作:
createTask/updateTask/deleteTask/triggerTask - 记录查询:
fetchRecords(taskId?, page, limit)— 含分页转换 - 日志查询:
fetchLogs(taskId?, level?, limit) - 告警规则操作:
createAlertRule/updateAlertRule/deleteAlertRule
3.1.4 UI 组件库
基于 shadcn/ui 体系,核心使用的组件包括:
Button、Card、Tabs、Table、Dialog、Sheet、Select、Switch、Badge、Skeleton、Tooltip、Separator、Toast / Toaster
图表组件基于 Recharts:
HourlyChart—BarChart(堆叠柱状图,成功/失败分层)SpiderChart—BarChart(横向柱状图,按爬虫名称统计)StatusPieChart—PieChart(环形图,HTTP 状态码分布)
3.2 后端模块(Crawler Scheduler)
爬虫调度微服务是一个独立的 Bun HTTP 服务,运行在端口 3002,主要承担三部分职责:HTTP API、爬虫调度 和 告警评估。
3.2.1 启动流程
start()
├── seedDatabase() ← 首次运行填充种子数据
├── Bun.serve() ← 启动 HTTP 服务 (port 3002)
├── setInterval(30s) ← 启动调度循环
├── runSchedulerCycle() ← 立即执行首个调度周期
└── await new Promise() ← 保持进程存活
3.2.2 HTTP API 路由表
| 方法 | 路径 | 处理函数 | 说明 |
|---|---|---|---|
| GET | / |
handleHealthCheck |
健康检查,返回运行状态和已运行时长 |
| GET | /api/tasks |
handleListTasks |
获取所有任务(含记录/日志计数) |
| POST | /api/tasks |
handleCreateTask |
创建新任务 |
| PATCH | /api/tasks/:id |
handleUpdateTask |
更新任务 |
| DELETE | /api/tasks/:id |
handleDeleteTask |
删除任务(级联删除记录和日志) |
| POST | /api/tasks/:id/trigger |
handleTriggerTask |
手动触发一次爬取 |
| GET | /api/records |
handleListRecords |
分页查询爬取记录 |
| GET | /api/records/:id |
handleGetRecord |
获取单条记录详情(含 rawHtml) |
| GET | /api/records/:id/download |
handleDownloadRecord |
下载爬取结果的 HTML 文件 |
| GET | /api/logs |
handleListLogs |
查询日志(支持 taskId/level 过滤) |
| GET | /api/metrics |
handleGetMetrics |
获取聚合指标数据 |
| GET | /api/alerts |
handleListAlerts |
查询告警列表 |
| GET | /api/alert-rules |
handleListAlertRules |
获取告警规则列表 |
| POST | /api/alert-rules |
handleCreateAlertRule |
创建告警规则 |
| PATCH | /api/alert-rules/:id |
handleUpdateAlertRule |
更新告警规则 |
| DELETE | /api/alert-rules/:id |
handleDeleteAlertRule |
删除告警规则 |
3.2.3 爬虫调度引擎
// 调度循环 (每隔 30 秒执行一次)
async function runSchedulerCycle() {
// 1. 查询所有 active 状态的任务
const activeTasks = await db.crawlTask.findMany({ where: { status: "active" } });
// 2. 逐个执行爬取
for (const task of activeTasks) {
await executeCrawl(task);
}
// 3. 执行告警评估
await evaluateAlertRules();
}
executeCrawl 执行流程:
- 调用
simulateCrawl(task)获取模拟爬取结果 - 写入
CrawlRecord记录 - 写入
CrawlLog日志 - 更新任务的
lastRunAt和lastStatus
3.2.4 爬取模拟器
simulateCrawl 基于概率分布模拟真实爬取行为:
| 场景 | 概率 | 状态码 | 响应时间 | 日志级别 |
|---|---|---|---|---|
| 成功 | 85% | 200 | 0.1s ~ 3s(90%)或 5s ~ 15s(10%) | info |
| 禁止访问 | 8% | 403 | 正常分布 | warning |
| 超时 | 5% | 0(超时标记) | 正常分布 | error |
| 其他错误 | 2% | 400 ~ 599 | 正常分布 | error |
成功爬取时,根据爬虫类型(spiderName)生成对应风格的 HTML 模板内容,包含:
default_spider— 通用网页tech_news_spider— 科技新闻文章ecommerce_spider— 电商产品页(含价格、评分、规格)social_spider— 社交媒体帖子(含头像、互动数据)blog_spider— 技术博客(含目录、代码块、作者信息)docs_spider— API 文档(含侧边栏、端点表格)
3.2.5 告警评估引擎
evaluateAlertRules 在每个调度周期末尾执行:
- 查询所有启用的告警规则
- 统计最近 30 分钟的爬取记录,计算成功率、错误数、平均响应时间
- 逐条匹配规则条件(lt / gt / eq)
- 触发告警时检查 5 分钟内是否有相同规则的 firing 告警,避免重复
3.2.6 进程管理
manager.ts 负责守护 crawler-scheduler 主进程:
- 通过
spawn启动子进程 - 将子进程的输出转发到标准输出并写入日志文件
/tmp/crawler-scheduler.log - 子进程退出时等待 3 秒后退出自身,由
start.shwrapper 重新拉起
4. 数据模型设计
4.1 ER 关系图
CrawlTask (1) ──┬── (N) CrawlRecord
│
└── (N) CrawlLog
AlertRule (1) ──── (N) Alert
4.2 CrawlTask(爬虫任务)
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | String (CUID) | PK | 主键 |
| name | String | NOT NULL | 任务名称 |
| url | String | NOT NULL | 目标 URL |
| cronExpr | String | 默认 “*/30 * * * *” | Cron 表达式 |
| status | String | 默认 “active” | active / paused / error |
| spiderName | String | 默认 “default_spider” | 爬虫类型标识 |
| concurrency | Int | 默认 1 | 并发数 |
| timeout | Int | 默认 15 | 超时时间(秒) |
| lastRunAt | DateTime? | - | 最近一次执行时间 |
| nextRunAt | DateTime? | - | 下一次计划执行时间 |
| lastStatus | String? | - | 最近一次执行结果 |
| createdAt | DateTime | 默认 now() | 创建时间 |
| updatedAt | DateTime | 自动更新 | 更新时间 |

4.3 CrawlRecord(爬取记录)
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | String (CUID) | PK | 主键 |
| taskId | String | FK → CrawlTask, ON DELETE CASCADE | 关联任务 |
| url | String | NOT NULL | 被抓取 URL |
| title | String? | - | 页面标题 |
| contentHash | String? | INDEX | 内容哈希值 |
| statusCode | Int? | - | HTTP 状态码 |
| duration | Float? | - | 耗时(秒) |
| rawHtml | String? | - | 原始 HTML 内容 |
| isNew | Boolean | 默认 true | 是否为新内容 |
| crawledAt | DateTime | 默认 now() | 爬取时间 |
索引:taskId、contentHash

4.4 CrawlLog(爬取日志)
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | String (CUID) | PK | 主键 |
| taskId | String | FK → CrawlTask, ON DELETE CASCADE | 关联任务 |
| level | String | 默认 “info” | info / warning / error |
| message | String | NOT NULL | 日志消息 |
| errorType | String? | - | 错误类型 |
| url | String? | - | 目标 URL |
| duration | Float? | - | 耗时(秒) |
| statusCode | Int? | - | HTTP 状态码 |
| createdAt | DateTime | 默认 now() | 创建时间 |
索引:taskId、level
4.5 AlertRule(告警规则)
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | String (CUID) | PK | 主键 |
| name | String | NOT NULL | 规则名称 |
| metric | String | NOT NULL | 指标:success_rate / error_count / response_time |
| condition | String | NOT NULL | 条件:lt / gt / eq |
| threshold | Float | NOT NULL | 阈值 |
| duration | Int | 默认 300 | 评估窗口(秒) |
| severity | String | 默认 “warning” | critical / warning / info |
| enabled | Boolean | 默认 true | 是否启用 |
| createdAt | DateTime | 默认 now() | 创建时间 |
| updatedAt | DateTime | 自动更新 | 更新时间 |
4.6 Alert(告警记录)
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | String (CUID) | PK | 主键 |
| ruleId | String | FK → AlertRule | 关联规则 |
| message | String | NOT NULL | 告警消息 |
| severity | String | 默认 “warning” | 严重程度 |
| status | String | 默认 “firing” | firing / resolved |
| value | Float? | - | 触发值 |
| triggeredAt | DateTime | 默认 now() | 触发时间 |
| resolvedAt | DateTime? | - | 解决时间 |
索引:ruleId、status
5. 反向代理设计
系统使用 Caddy 作为反向代理,监听 81 端口,支持两种路由模式:
5.1 默认路由
未携带 XTransformPort 查询参数时,请求转发到 Next.js(端口 3000),并携带原始请求头(Host、X-Forwarded-For、X-Forwarded-Proto、X-Real-IP)。
5.2 动态端口路由
携带 XTransformPort 参数时(如 ?XTransformPort=3002),请求转发到对应端口的 localhost 服务。此机制用于开发调试时灵活切换后端目标。
6. 种子数据
系统首次启动时自动执行 seedDatabase(),填充以下种子数据:
6.1 示例任务(5 个)
| 任务名称 | URL | Cron | 爬虫类型 | 默认状态 |
|---|---|---|---|---|
| Tech News Crawler | https://news.example.com | */15 * * * * | tech_news_spider | active |
| E-Commerce Products | https://shop.example.com/products | */30 * * * * | ecommerce_spider | active |
| Social Media Feed | https://social.example.com/feed | */5 * * * * | social_spider | active |
| Blog Articles | https://blog.example.com | 0 * * * * | blog_spider | paused |
| API Documentation | https://docs.example.com | 0 */2 * * * | docs_spider | active |
6.2 告警规则(3 条)
| 规则名称 | 指标 | 条件 | 阈值 | 严重程度 |
|---|---|---|---|---|
| Success Rate Alert | success_rate | lt | 80 | critical |
| Error Spike Alert | error_count | gt | 10 | warning |
| Slow Response Alert | response_time | gt | 5 | warning |
6.3 历史数据
- 50 条爬取记录(CrawlRecord),时间随机分布在过去 24 小时内
- 30 条爬取日志(CrawlLog),时间随机分布在过去 24 小时内
7. 构建与部署流程
7.1 构建流程(build.sh)
1. bun install → 安装 Node 依赖
2. bun run build → Next.js standalone 构建
3. mini-services-install.sh → 微服务依赖安装
4. mini-services-build.sh → 微服务 bun build 打包
5. 收集产物到临时目录
├── next-service-dist/ → Next.js standalone 输出
├── mini-services-dist/ → 微服务构建产物
├── db/custom.db → SQLite 数据库
├── Caddyfile → Caddy 配置
├── start.sh → 生产启动脚本
└── mini-services-start.sh → 微服务启动脚本
6. tar -czf 打包为 .tar.gz
7.2 启动流程(start.sh)
1. 设置环境变量(PORT, HOSTNAME, DATABASE_URL)
2. 启动 Next.js 服务 → bun server.js (port 3000)
3. 启动 mini-services → sh mini-services-start.sh
4. 启动 Caddy 反向代理 → caddy run (port 81, 前台运行)
7.3 微服务启动流程(mini-services-start.sh)
扫描 mini-services-dist/ 目录下的 mini-service-*.js 文件,逐个通过 bun 启动为后台进程。
7.4 进程守护机制
crawler-scheduler 采用三层守护:
start.sh (wrapper)
└── while true; bun manager.ts
└── spawn('bun', ['index.ts'])
- 内层 index.ts 崩溃 → manager.ts 检测到 exit,3 秒后退出
- manager.ts 退出 → start.sh 检测到退出码,2 秒后重新拉起
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)