Files
getDomain/docs/16_domainCheck_多机检测与跨地域部署设计.md
Your Name ebf632e651 first
2026-04-16 21:35:47 +08:00

797 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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不再重构核心架构。