This commit is contained in:
Your Name
2026-04-16 21:35:47 +08:00
parent ff32aa50bf
commit ebf632e651
86 changed files with 14097 additions and 585 deletions

View File

@@ -0,0 +1,796 @@
# 16 domainCheck 多机检测与跨地域部署设计
## 一、目标
本文档用于固化 `domainCheck` 的正式多机落地方案,满足下面这些约束:
- Web 后台、管理 API 必须部署在国外机器
- 检测执行、代理、Redis、运行态存储部署在大陆机器
- 最终结果、配置、归档、备份以国外机器为主
- 初期部署不复杂,支持从 `国外 1 台 + 大陆 1 台` 起步
- 后期不够时,优先横向增加大陆 Worker不推翻整体架构
一句话原则:
> 国外负责“管、看、存、备份”,大陆负责“跑、调度、抢任务、贴近代理资源”。
## 二、当前落地状态
截至 `2026-04-16`,第一阶段多机运行骨架已经完成,当前代码已具备:
- 控制面节点注册与心跳
- 执行面节点注册与心跳
- 运行库骨架表自动初始化
- 集群节点快照接口
- 国外/大陆两套部署脚手架
当前已落地的运行态表:
- `detect_worker_nodes`
- `detect_jobs`
- `detect_job_items`
- `detect_run_events`
- `detect_sync_records`
当前可直接验证的接口:
- `GET /api/v1/runtime/status`
- `GET /api/v1/runtime/readiness`
- `GET /api/v1/runtime/cluster`
- `GET /api/v1/detect/job/active`
- `GET /api/v1/detect/jobs`
- `GET /api/v1/detect/queue-summary`
当前 `runtime/cluster` 还补充了 `summary` 摘要,至少会返回:
- `status_counts`
- `role_counts`
- `region_counts`
- `online_worker_nodes`
- `online_control_nodes`
- `busy_nodes`
- `stale_nodes`
- `offline_nodes`
当前还补充了同步观测骨架:
- `GET /api/v1/runtime/sync-summary`
- `GET /api/v1/runtime/sync-records`
可用于提前观察:
- 当前节点是否启用同步推送
- 源地域 / 目标地域配置是否正确
- `detect_sync_records` 最近状态分布
当前代码还补了一层“运行态自动投影”:
- 控制面在读取 `runtime/status` 时,会把当前运行摘要按去重策略写入 `detect_sync_records`
- 当前记录类型为:
- `runtime_projection`
- 当前还新增了“结果摘要投影”:
- `detect_result_projection`
- 用于把当前活跃任务的摘要、最近阶段事件、任务收敛进度先沉淀到统一同步面板
- 其作用不是替代后续真正的 `sync-service`
- 而是先保证跨地域部署阶段已经有统一可观察的“同步记录面板”
当前还补上了正式同步最小闭环骨架:
- 大陆执行面可通过 `SYNC_TARGET_API_BASE_URL` 指向国外控制面
-`SYNC_PUSH_ENABLED=true`,应由大陆 `domaincheck-sync-agent` 按轮询间隔自动推送最新运行态投影
- 国外控制面通过:
- `POST /api/v1/runtime/sync-ingest`
接收投影
- 若配置了 `SYNC_SHARED_TOKEN`,接收端会校验:
- `X-Domaincheck-Sync-Token`
因此当前虽然还没有完整业务结果同步服务,但“同步协议、发送端、接收端、记录面板、独立同步代理”都已经有了第一版可运行骨架
当前同步骨架已经支持的记录类型包括:
- `runtime_projection`
- `runtime_ingest`
- `detect_result_projection`
- `detect_result_ingest`
- `runtime_push`
当前这一轮还补上了“结果批次同步可观测”能力:
- `GET /api/v1/runtime/sync-summary` 现在会额外返回:
- `detect_result_batches`
- 可直接按最近检测任务查看:
- 是否已生成结果投影
- 是否已推送
- 是否已被目标地域接收
- 最近一次失败原因
- 当前同步策略也已细化为:
- `runtime_projection`
只推最新一条,避免把历史运行态投影整批补传
- `detect_result_projection`
按批次补推,确保检测结果积压可以自动追平
- 大陆 `controller` 的环境模板也已固定为:
- `NODE_ROLE=control`
避免把 controller 误注册成普通 Worker导致运行中心和 sync-agent 承载判断失真
当前这一轮还额外补齐了“检测会话追踪”能力:
- `POST /api/v1/detect/start` 会生成本轮 `cycle_token`
- `cycle_token` 会进入 Worker 控制指令
- Worker 心跳元数据会回写 `cycle_token / job_id / job_code`
- `domain_started / domain_completed / domain_failed` 事件会附带 `cycle_token`
- `GET /api/v1/detect/job/active` 已支持按当前 `cycle_token` 聚合“本轮事件”
当前运行态还补齐了“代理执行语义”:
- 代理可用时显示 `代理正常`
- 代理暂时不可用但允许直连时显示 `降级直连`
- 代理暂时不可用且不允许直连时显示 `等待代理`
- 当代理源全部 `HTTP 200``raw_items=0` 时,会额外标记为“供应池为空”,避免误判成程序异常
当前阶段的准确表述应为:
> 多机运行骨架已经落地,分布式调度与跨地域结果同步进入下一阶段。
截至当前这一轮代码,最小任务闭环也已经接上:
- `POST /api/v1/detect/start` 会先创建 `detect_jobs`
- Worker 会优先从 `detect_job_items` 领取任务
- 运行中心已能显示当前活跃任务摘要
- 节点状态里已能反映 `busy / current_load`
- Worker 已支持小批量领取、任务续租、过期租约回收
- API 控制面节点已支持定时心跳,避免集群视图把控制面误判为离线
- 活跃任务接口已支持节点分布、进度百分比和最近事件回看
- Worker 进程重启后会主动回收当前节点遗留的 `running / claimed` 运行态,避免后台出现“进程已重启但旧任务仍显示运行中”的残影
这意味着后续新增第二台大陆 Worker 时,已经不再是“完全从零设计”的状态,而是可以在现有骨架上继续细化调度策略。
当前最小联调命令也已经固定为:
```bash
cd /opt/domaincheck/domain-api
bash deploy/multi-region/check_cluster.sh http://127.0.0.1:8100
```
这条命令会连续检查:
- `/health`
- `/api/v1/runtime/cluster`
- `/api/v1/detect/status`
- `/api/v1/detect/job/active`
如需单独检查“跨地域同步骨架是否已接上”,建议再补两条:
```bash
curl http://127.0.0.1:8100/api/v1/runtime/sync-summary
curl http://127.0.0.1:8100/api/v1/runtime/sync-records?limit=10
```
如需快速拿到“当前这套多机部署是否已进入可联调 / 待处理 / 阻断”结论,当前也已经固定了:
```bash
curl http://127.0.0.1:8100/api/v1/runtime/readiness
```
该接口会直接给出:
- `status`
- `ready`
- `attention`
- `blocking`
- `summary`
- `blocking_issues`
- `warnings`
- `info`
如果这两条接口已经能看到:
- `source_region / target_region`
- `records_total > 0`
- 最近记录里出现 `runtime_projection`
则说明当前测试服已经具备:
- 同步配置观测入口
- 同步记录落库入口
- 后续接入正式 `sync-service` 的最小运行骨架
如果要在大陆 controller 节点本机确认“这台机器本身是否配成了 controller而不是普通 Worker”当前也已经固定了一条本机检查命令
```bash
cd /opt/domaincheck/domain-api
bash deploy/multi-region/check_mainland_controller.sh
```
这条命令会直接校验:
- `/etc/default/domaincheck-worker`
- `NODE_ROLE=control`
- `SYNC_PUSH_ENABLED=true`
- `SYNC_TARGET_API_BASE_URL` 是否已填写
- `domaincheck-worker`
- `domaincheck-sync-agent`
是否已经启动
如果当前客观条件下还不能把代码真正部署到大陆机器,当前也支持先在国外测试机上做“模拟多节点联调”:
```bash
cd /opt/domaincheck/domain-api
bash deploy/multi-region/simulate_multi_region.sh http://127.0.0.1:8100
```
这条命令会临时模拟:
- 一个大陆 `controller`
- 一个大陆 `worker`
它的目的不是替代真实部署,而是先把下面这些提前跑通:
- `runtime/readiness`
- `runtime/cluster`
- 运行中心的多机就绪度提示
- 文档、脚本、状态面板三者是否一致
如果测试服之前已经跑过旧节点、旧 worker当前还残留陈旧心跳记录也可以先清一次 `detect_worker_nodes` 里的历史残影,再观察 readiness
```bash
cd /opt/domaincheck/domain-api
bash deploy/multi-region/prune_cluster_nodes.sh --minutes 30 --dry-run
bash deploy/multi-region/prune_cluster_nodes.sh --minutes 30
```
它的作用是:
- 清掉很久没心跳的旧节点记录
- 避免运行中心长期被历史离线节点污染成 `attention`
- 让单机模拟多节点联调时,看到更接近真实接入后的状态
## 三点五、当前实测结论
截至 `2026-04-16 18:56`,测试服已经验证通过:
- `domaincheck-api``domaincheck-worker` 可在 `systemd` 下稳定重启
- `worker_mode=linux-systemd`
- `runtime/cluster` 可同时看到:
- `overseas-control-01`
- `mainland-worker-01`
- 大陆 Worker 节点元数据已显示:
- `phase`
- `detail`
- `job_id`
- `job_code`
- `cycle_token`
- `detect/job/active``current_cycle_events` 已只回看当前这一轮检测事件,不再混入旧轮次历史
当前测试服最新一轮实测 `cycle_token` 示例:
- `aec105737f`
该轮已确认能看到:
- `job_dispatch_requested`
- `job_dispatch_sent`
- `domain_started`
- `domain_completed`
这说明“控制面发起一次检测”到“执行面逐域名回写事件”的会话闭环已经打通。
也说明当前后台已经不再只会显示“有告警”,而是能明确说明任务到底是在正常代理、降级直连,还是被代理卡住。
## 四、推荐架构
### 1. 国外控制面
国外机器负责:
- `domain-web`
- `domain-api`
- 国外主 PostgreSQL
- 配置中心
- 导出、审核、运营后台
- 最终结果归档
- 国外备份与日志归档
国外控制面的职责:
- 对外提供访问入口
- 保存最终业务结果
- 保存系统配置和审计记录
- 接收大陆执行面的结果同步
- 为后续灾备提供稳定的权威数据源
### 2. 大陆执行面
大陆机器负责:
- `detect-worker`
- 大陆 Redis
- 大陆运行库 PostgreSQL
- 代理池配置与网络环境
- 本地运行日志
- 调度和任务租约
大陆执行面的职责:
- 抢任务
- 续租
- 运行检测链路
- 写入本地运行态
- 汇总并同步结果到国外
### 3. 初期物理部署
当前最优起步方案:
- 国外 `1` 台:
- `domain-web`
- `domain-api`
- `postgresql_main`
- 大陆 `1` 台:
- `redis`
- `postgresql_runtime`
- `scheduler`
- `detect-worker`
说明:
- 这个起步方案的优先目标是先把跨地域职责边界定清楚
- 大陆机器当前可以先把 Redis、运行库、调度和 Worker 放在同一台
- 但逻辑上必须按不同角色设计,避免后续扩容时重新返工
- 各节点身份建议统一通过 `/etc/default/domaincheck-api``/etc/default/domaincheck-worker` 管理,不直接修改 service 文件正文
### 4. 后续扩容
当大陆执行压力不够时,扩容顺序建议为:
1. 新增大陆 Worker 机器
2. 如果 Redis 压力增大,再拆 Redis
3. 如果运行库写入压力增大,再拆运行库
4. 国外主库继续作为最终结果主库,不参与高频任务调度
后续结构示意:
```text
国外
├── web + api + postgresql_main
└── backup / archive
大陆
├── redis + postgresql_runtime + scheduler
├── worker-01
├── worker-02
├── worker-03
└── worker-N
```
## 五、数据分层
### 1. 结果态数据
这类数据保存在国外主库:
- 域名主数据
- 检测最终结果
- 黑名单结论
- 导出记录
- 审核记录
- 系统配置
- 操作审计
特点:
- 最终一致
- 生命周期长
- 提供给后台页面和运营使用
### 2. 运行态数据
这类数据优先保存在大陆执行面:
- 任务领取状态
- 任务租约
- 节点心跳
- 当前阶段
- 实时日志事件
- 重试信息
- 本地暂存结果
特点:
- 高频读写
- 延迟敏感
- 主要服务于 Worker 协作和调度
## 六、任务模型
当前项目还偏向单机 Worker 直接扫描 `domains` 表的模式。正式多机化后,建议改为显式任务模型。
### 1. 核心表建议
建议新增:
- `detect_jobs`
- `detect_job_items`
- `detect_worker_nodes`
- `detect_run_events`
- `detect_sync_records`
### 2. detect_jobs
表示一轮检测任务。
建议字段:
- `id`
- `job_code`
- `source`
- `plan_hash`
- `status`
- `created_at`
- `started_at`
- `finished_at`
- `created_by`
- `remark`
说明:
- `plan_hash` 用于标识本轮检测所使用的规则版本和参数快照
- 一轮任务可以对应大量域名任务项
### 3. detect_job_items
表示单个域名的具体任务项。
建议字段:
- `id`
- `job_id`
- `domain_id`
- `status`
- `claimed_by`
- `claim_token`
- `lease_expires_at`
- `attempt_count`
- `last_error`
- `started_at`
- `finished_at`
- `result_version`
- `updated_at`
建议唯一约束:
- `unique(job_id, domain_id)`
说明:
- 多台 Worker 只抢 `status=pending` 且未被占用或租约已过期的任务
- `claim_token` 用于避免状态回写串单
- `lease_expires_at` 用于机器故障后的任务回收
### 4. detect_worker_nodes
表示 Worker 节点。
建议字段:
- `node_code`
- `region`
- `role`
- `hostname`
- `ip`
- `status`
- `worker_version`
- `last_heartbeat_at`
- `current_load`
- `remark`
说明:
- 后台后续可以直接展示“哪个节点在线、哪个节点在跑、当前负载多少”
### 5. detect_run_events
表示高价值阶段日志。
建议字段:
- `id`
- `job_id`
- `job_item_id`
- `node_code`
- `event_type`
- `level`
- `message`
- `payload_json`
- `created_at`
说明:
- 页面展示优先依赖事件流,不直接硬依赖某一台机器本地日志文件
- 原始大日志仍保留在大陆 Worker 本地
## 七、多机抢任务与去重
### 1. 抢任务方式
推荐使用数据库租约模型:
- Worker 从 `detect_job_items` 中领取一批待处理任务
- 使用 `FOR UPDATE SKIP LOCKED` 或原子更新方式抢占
- 领取后写入:
- `claimed_by`
- `claim_token`
- `lease_expires_at`
### 2. 去重原则
多机环境下,去重不能依赖“约定不重复”,必须依赖数据库约束和租约。
至少保证:
- 同一轮 `job_id` 下,同一域名只出现一条任务项
- 同一任务项同一时刻只允许被一个 `claim_token` 持有
- 回写结果时校验 `claim_token`
### 3. 续租机制
Worker 执行过程中应定期续租:
-`30-60` 秒续租一次
- 续租时更新 `lease_expires_at`
- 如果节点挂掉,租约到期后其他 Worker 可重新领取
当前代码已实现的原则是:
- 任务被领取后进入 `claimed / running`
- 检测步骤执行过程中会持续续租
- 若任务长期未续租且租约过期,会自动回收到 `pending`
这样可以避免:
- 长任务跑到一半租约失效
- Worker 异常退出后任务永远卡死
- 后续多机扩容时出现大量“假占用”
### 4. 失败重试
建议对失败进行分类:
- 网络失败
- 代理失败
- 第三方目标站异常
- 程序逻辑失败
可按类型控制重试次数,不要无限重试。
### 5. 当前代码与目标模型的关系
当前代码里,运行态表和节点心跳已经具备,但主检测流程仍以现有 `domains` / `detect_status` 链路为主。
这意味着:
- 现在已经可以先把“多机可见性”和“节点骨架”跑起来
- 下一步要把“检测入口”从直接扫 `domains`,逐步切到 `detect_jobs + detect_job_items`
- 切换时必须保留旧链路回退能力,避免一次性重写导致现网不稳
## 八、同步策略
### 1. 国外到大陆
同步内容:
- 系统配置
- 检测规则
- 敏感词版本
- 任务创建命令
特点:
- 低频
- 要求可靠
- 可版本化
### 2. 大陆到国外
同步内容:
- 任务进度摘要
- 关键阶段事件
- 最终检测结果
- 黑名单结论
- 归档日志包索引
特点:
- 高频部分只传摘要和关键事件
- 最终结果必须幂等
### 3. 幂等要求
结果同步到国外时,必须支持幂等:
- 同一 `job_item_id` 重复上报不应产生重复结果
- 同一域名同一轮任务只保留最终有效状态
### 4. 推荐同步落地方式
第一阶段不建议直接做数据库双向复制,更推荐独立同步服务:
- 大陆执行面负责写本地运行态
- `sync-service` 负责批量汇总结果与事件
- 国外控制面负责接收、落库、归档
推荐第一阶段同步内容:
- 最终检测结果
- 关键阶段事件
- 失败摘要
- 节点健康摘要
- 配置快照版本
不建议第一阶段同步:
- 全量原始日志
- 高频心跳明细
- 临时抓取中间产物
原因:
- 成本高
- 跨地域网络抖动会放大耦合
- 对运营后台价值不成比例
## 九、部署建议
### 1. 国外机器
目录建议:
```text
/opt/domaincheck
├── domain-api
├── domain-web
└── shared
```
部署角色:
- `web`
- `api`
- `postgresql_main`
### 2. 大陆调度中心
目录建议:
```text
/opt/domaincheck
├── domainCheck
├── runtime-db
├── redis
└── shared
```
部署角色:
- `scheduler`
- `redis`
- `postgresql_runtime`
- `worker`
### 3. 大陆纯 Worker 节点
目录建议:
```text
/opt/domaincheck
├── domainCheck
└── shared
```
部署角色:
- `worker`
### 4. 一键化原则
部署脚本必须满足:
- 新机器只需要少量环境变量
- 不要求现场手工改很多路径
- 国外机和大陆机各自有独立入口脚本
- 后续新增大陆 Worker 节点继续复用同一脚本
### 5. 当前建议的最小部署
当前最适合正式起步的形态:
- 国外 `1` 台:
- `domain-web`
- `domain-api`
- 国外主 PostgreSQL
- 大陆 `1` 台:
- `redis`
- 大陆运行库 PostgreSQL
- `detect-worker`
- 后续可补 `sync-service`
扩容时优先:
1. 新增大陆 Worker
2. 再视压力拆分大陆运行库与 Redis
3. 国外控制面保持稳定,不跟着高频扩缩
## 十、阶段性落地建议
### 第一阶段
先完成:
- 国外控制面部署
- 大陆单机执行面部署
- 节点注册与心跳
- 当前后台继续可用
### 第二阶段
再完成:
- 任务项模型
- 多 Worker 抢任务
- 事件日志流
- 结果异步同步回国外
### 第三阶段
最后完成:
- 大陆多 Worker 横向扩容
- 运行库与调度中心独立拆分
- 国外归档和监控补齐
## 十一、运行与观测建议
多机之后,不能再只靠“登录某台机器 tail 日志”来判断系统状态。
建议分层:
- 大陆 Worker 本地保留完整原始日志
- `detect_run_events` 保存关键阶段事件
- 国外后台优先展示事件流、节点负载和同步状态
- 真排障时再下钻到具体节点原始日志
后台建议优先展示:
- 当前在线节点数
- 每节点最近心跳
- 每节点当前负载
- 当前活跃任务数
- 队列积压与最老待领年龄
- 租约是否过期、是否即将到期
- 近 15 分钟每节点吞吐
- 最近失败摘要
- 最近同步结果
- 最近结果摘要投影
## 十二、下一阶段开发清单
接下来建议按下面顺序继续落地:
1. 后台创建 `detect_jobs / detect_job_items`
2. Worker 实现批量 claim / renew / finish / fail
3. 增加租约超时回收
4. 将检测阶段事件写入 `detect_run_events`
5. 增加 `sync-service`
6. 完成第二台大陆 Worker 接入演练
## 十三、最终建议
对当前项目来说,最优解不是“全部放国外”,也不是“全部放大陆”,而是:
- 国外做控制面和主结果库
- 大陆做执行面和运行态调度
- 先从 `国外 1 台 + 大陆 1 台` 起步
- 后续优先横向增加大陆 Worker
一句话总结:
> 先把职责边界设计对,再让部署简单;后续扩容时只加 Worker不再重构核心架构。