Files
2026-09-29 17:16:50 +08:00

6.8 KiB

进程与健康

控制台「运行日志 → 进程与健康」的数据来源。后端在 apps/server/src/modules/system/,两个端点都只给 admin。

心跳怎么写

进程 谁写 process_kind
--role api system 模块注册路由时起写入器 api
--role worker(io 组) worker/index.ts worker
--role worker:agent worker/index.ts worker:agent
--role all worker/index.ts(同进程的 API 不再单写) all
  • 每 15 秒 upsert process_heartbeat 一行,同时往 process_heartbeat_sample 追加一条采样。
  • 行主键 instance_id = <kind>:<host>:<pid>,容器重启覆盖同一行。
  • 正常退出(SIGTERM / SIGINT)时写入器把本行标 stopped_at,读取方立即归入已下线实例;重启后同一行的 upsert 把它清回 null。
  • 裁剪由定时任务 system.heartbeat_cleanup(维护组,1 小时)负责:24 小时没心跳的进程行删除,采样保留 storage.retentionDays.heartbeatHours(缺省 24 小时,CACHE-2 前是 7 天)。
  • 写库失败只记 debug 日志,不影响业务。

GET /system/health 字段

字段 含义
level 各段最差档:ok / warn / critical。取不到数据的段是 unknown,不参与汇总
api 处理本次请求的 API 进程:版本、启动时间、运行秒数
processes[] 当前实例:最近 10 分钟内有心跳的 host 上、同 host 同 kind 最新启动且没标 stopped_at 的进程
processes[].stale 超过 45 秒没心跳。只有当前实例参与告警
processes[].stoppedAt 正常退出时间;非 null 即已下线
retiredProcesses[] 已下线实例:正常退出的、所在 host 10 分钟没有心跳(容器已重建)的、被同 host 新进程替换的。不计入 level,页面折叠显示
processes[].eventLoopLagMs 最近一次采样的事件循环延迟(1 秒采一次)
processes[].eventLoopLagMaxMs 最近一个心跳周期(15 秒)内的最大延迟
processes[].concurrency 引擎 worker 并发 {io, agent};api 进程为 {}
processes[].runningRuns 本组任务正在执行的 run 数。同组多实例时是整组合计;api 进程为 null
processes[].sseConnections 本进程打开的 SSE 流数。不托管 HTTP 的进程为 null
processes[].extra rssBytes、heapUsedBytes、lifetimeMaxLagMs,worker 另有 group、lagWarnings、probeMaxLagMs
sse.connections 存活 api/all 进程的 SSE 连接合计
db select 1 延迟、库大小、本库连接数(按 active / idle / idle in transaction 分)与 max_connections
disks[] DATA_DIR、每个启用的库根、每个下载器路径映射的 statfs 用量
diskThresholds 取自设置 cache.resumePercent(warn)和 cache.hardThresholdPercent(critical)
mounts[] 库根做哨兵检查(checkRoot),下载目录只查目录可读。status:online / offline / timeout
engine 待处理 run 数、执行中 run 数、最老待处理时长、24 小时结束数 / 失败数 / 失败率
schedules 定时任务总数、启用数、执行中数、最近一次失败的启用任务、24 小时失败运行数
storage 存储预算档位与告警文案(PG 库体积、图片缓存、备份、会话与证据对照 settings.storage.budgets),明细见 GET /system/storage(storage.md)

单个路径的 statfs 或哨兵检查超过 3 秒记 timeout:磁盘段标 critical,挂载段标 timeout。请求本身不会被卡住。

GET /system/health/history?hours=

  • hours 取 1–168,缺省 6。采样只保留 heartbeatHours(缺省 24 小时),更早的部分为空。
  • 按桶聚合,每条序列最多约 360 个点。桶宽最小 15 秒,168 小时时是 1680 秒。
  • 每个进程一条序列,每个点有 lagMs(桶内平均)、lagMaxMs(桶内最大)、runningRuns、sseConnections。

告警阈值建议

  • 当前实例 stale:warn。连续 2 分钟仍 stale 就去查容器状态和日志。容器整个没了 10 分钟后它的进程转入已下线实例,此时靠下一条"没有存活的 worker"兜底。
  • 没有任何存活的 worker / all 进程:warn。任务不会被执行。
  • 事件循环最大延迟 ≥ 1 秒记 warn,≥ 5 秒记 critical。延迟接近 15 秒时,引擎租约有丢失风险。
  • DB 延迟 ≥ 100 ms 或连接数 ≥ 75% max_connections 记 warn;≥ 500 ms 或 ≥ 90% 记 critical。
  • idle in transaction 连接超过 5 个记 warn,通常是有事务没提交。
  • 磁盘用量 ≥ cache.resumePercent(缺省 85%)记 warn,≥ cache.hardThresholdPercent(缺省 90%)记 critical。
  • 挂载根 offline 或 timeout:critical。先查宿主机挂载,再查哨兵文件。
  • 最老待处理 run 等待 ≥ 5 分钟记 warn,≥ 30 分钟记 critical。通常是 worker 没起来或并发被占满。
  • 24 小时失败率 ≥ 10% 记 warn;失败率 ≥ 30% 且结束数 ≥ 10 记 critical。
  • 有启用的定时任务最近一次运行失败:warn。
  • PG 库体积 ≥ pgDataWarnGb(20 GB)记 warn,≥ pgDataGb(25 GB)记 critical;备份、会话与证据 ≥ 预算 80% 记 warn、≥ 预算记 critical; 图片缓存超预算 10% 记 warn。

引擎 runs 的业务字段

GET /engine/runs 和 GET /engine/runs/{id} 的每个 run 多了 business,推断不出时为 null:

{ "kind": "feed", "refId": "<feed_source.id>", "title": "HDSky RSS", "triggeredBy": "event" }
  • kind:subscription / download / organization / agent / schedule / feed / site / decision / media。
  • refId:业务对象 id;schedule 是定时任务 key(如 trash.purge)。download 优先指向下载任务,计划还没提交时指向收录计划 id。
  • title:业务对象的展示名。对象已删除时为 null。
  • triggeredBy:user / api_key / schedule / agent / event,推断不出为 null。

kind 与 refId 的推断顺序(auth-settings/engine-business.ts,列表筛选和展示用同一条 SQL 表达式):

  1. run 输入里的业务 id 键:agentRunId、recordId、downloadTaskId、acquisitionPlanId、subscriptionId、acquisitionSlotId、feedSourceId、siteId、accountId(换成站点)、decisionId、mediaEntityId。
  2. 业务任务行 task(engine_run_id = run id)的 subject_type / subject_id。
  3. 没有业务 id 的定时任务(purgeTrash、refreshPiTokens、cleanupDownloadCache、reconcileLibrary),且幂等键不是手动或重跑。

triggeredBy 的推断顺序:

  1. Agent 会话看 agent_run.trigger。
  2. 看 task.triggered_by。
  3. 幂等键是 retry: 或含 manual 的记为 user。
  4. 引擎幂等键能对上 outbox 事件的记为 event。
  5. 调度器时间槽键记为 schedule。

筛选参数:businessKind、businessRefId,可以一起用。这两个参数对每行都要算一次推断表达式,runs 很多时要带上时间范围。