19 KiB
16 domainCheck 多机检测与跨地域部署设计
一、目标
本文档用于固化 domainCheck 的正式多机落地方案,满足下面这些约束:
- Web 后台、管理 API 必须部署在国外机器
- 检测执行、代理、Redis、运行态存储部署在大陆机器
- 最终结果、配置、归档、备份以国外机器为主
- 初期部署不复杂,支持从
国外 1 台 + 大陆 1 台起步 - 后期不够时,优先横向增加大陆 Worker,不推翻整体架构
一句话原则:
国外负责“管、看、存、备份”,大陆负责“跑、调度、抢任务、贴近代理资源”。
二、当前落地状态
截至 2026-04-16,第一阶段多机运行骨架已经完成,当前代码已具备:
- 控制面节点注册与心跳
- 执行面节点注册与心跳
- 运行库骨架表自动初始化
- 集群节点快照接口
- 国外/大陆两套部署脚手架
当前已落地的运行态表:
detect_worker_nodesdetect_jobsdetect_job_itemsdetect_run_eventsdetect_sync_records
当前可直接验证的接口:
GET /api/v1/runtime/statusGET /api/v1/runtime/readinessGET /api/v1/runtime/clusterGET /api/v1/detect/job/activeGET /api/v1/detect/jobsGET /api/v1/detect/queue-summary
当前 runtime/cluster 还补充了 summary 摘要,至少会返回:
status_countsrole_countsregion_countsonline_worker_nodesonline_control_nodesbusy_nodesstale_nodesoffline_nodes
当前还补充了同步观测骨架:
GET /api/v1/runtime/sync-summaryGET /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_projectionruntime_ingestdetect_result_projectiondetect_result_ingestruntime_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_tokencycle_token会进入 Worker 控制指令- Worker 心跳元数据会回写
cycle_token / job_id / job_code domain_started / domain_completed / domain_failed事件会附带cycle_tokenGET /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 时,已经不再是“完全从零设计”的状态,而是可以在现有骨架上继续细化调度策略。
当前最小联调命令也已经固定为:
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
如需单独检查“跨地域同步骨架是否已接上”,建议再补两条:
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
如需快速拿到“当前这套多机部署是否已进入可联调 / 待处理 / 阻断”结论,当前也已经固定了:
curl http://127.0.0.1:8100/api/v1/runtime/readiness
该接口会直接给出:
statusreadyattentionblocking
summaryblocking_issueswarningsinfo
如果这两条接口已经能看到:
source_region / target_regionrecords_total > 0- 最近记录里出现
runtime_projection
则说明当前测试服已经具备:
- 同步配置观测入口
- 同步记录落库入口
- 后续接入正式
sync-service的最小运行骨架
如果要在大陆 controller 节点本机确认“这台机器本身是否配成了 controller,而不是普通 Worker”,当前也已经固定了一条本机检查命令:
cd /opt/domaincheck/domain-api
bash deploy/multi-region/check_mainland_controller.sh
这条命令会直接校验:
/etc/default/domaincheck-workerNODE_ROLE=controlSYNC_PUSH_ENABLED=trueSYNC_TARGET_API_BASE_URL是否已填写domaincheck-workerdomaincheck-sync-agent是否已经启动
如果当前客观条件下还不能把代码真正部署到大陆机器,当前也支持先在国外测试机上做“模拟多节点联调”:
cd /opt/domaincheck/domain-api
bash deploy/multi-region/simulate_multi_region.sh http://127.0.0.1:8100
这条命令会临时模拟:
- 一个大陆
controller - 一个大陆
worker
它的目的不是替代真实部署,而是先把下面这些提前跑通:
runtime/readinessruntime/cluster- 运行中心的多机就绪度提示
- 文档、脚本、状态面板三者是否一致
如果测试服之前已经跑过旧节点、旧 worker,当前还残留陈旧心跳记录,也可以先清一次 detect_worker_nodes 里的历史残影,再观察 readiness:
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-systemdruntime/cluster可同时看到:overseas-control-01mainland-worker-01
- 大陆 Worker 节点元数据已显示:
phasedetailjob_idjob_codecycle_token
detect/job/active的current_cycle_events已只回看当前这一轮检测事件,不再混入旧轮次历史
当前测试服最新一轮实测 cycle_token 示例:
aec105737f
该轮已确认能看到:
job_dispatch_requestedjob_dispatch_sentdomain_starteddomain_completed
这说明“控制面发起一次检测”到“执行面逐域名回写事件”的会话闭环已经打通。 也说明当前后台已经不再只会显示“有告警”,而是能明确说明任务到底是在正常代理、降级直连,还是被代理卡住。
四、推荐架构
1. 国外控制面
国外机器负责:
domain-webdomain-api- 国外主 PostgreSQL
- 配置中心
- 导出、审核、运营后台
- 最终结果归档
- 国外备份与日志归档
国外控制面的职责:
- 对外提供访问入口
- 保存最终业务结果
- 保存系统配置和审计记录
- 接收大陆执行面的结果同步
- 为后续灾备提供稳定的权威数据源
2. 大陆执行面
大陆机器负责:
detect-worker- 大陆 Redis
- 大陆运行库 PostgreSQL
- 代理池配置与网络环境
- 本地运行日志
- 调度和任务租约
大陆执行面的职责:
- 抢任务
- 续租
- 运行检测链路
- 写入本地运行态
- 汇总并同步结果到国外
3. 初期物理部署
当前最优起步方案:
- 国外
1台:domain-webdomain-apipostgresql_main
- 大陆
1台:redispostgresql_runtimeschedulerdetect-worker
说明:
- 这个起步方案的优先目标是先把跨地域职责边界定清楚
- 大陆机器当前可以先把 Redis、运行库、调度和 Worker 放在同一台
- 但逻辑上必须按不同角色设计,避免后续扩容时重新返工
- 各节点身份建议统一通过
/etc/default/domaincheck-api与/etc/default/domaincheck-worker管理,不直接修改 service 文件正文
4. 后续扩容
当大陆执行压力不够时,扩容顺序建议为:
- 新增大陆 Worker 机器
- 如果 Redis 压力增大,再拆 Redis
- 如果运行库写入压力增大,再拆运行库
- 国外主库继续作为最终结果主库,不参与高频任务调度
后续结构示意:
国外
├── 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_jobsdetect_job_itemsdetect_worker_nodesdetect_run_eventsdetect_sync_records
2. detect_jobs
表示一轮检测任务。
建议字段:
idjob_codesourceplan_hashstatuscreated_atstarted_atfinished_atcreated_byremark
说明:
plan_hash用于标识本轮检测所使用的规则版本和参数快照- 一轮任务可以对应大量域名任务项
3. detect_job_items
表示单个域名的具体任务项。
建议字段:
idjob_iddomain_idstatusclaimed_byclaim_tokenlease_expires_atattempt_countlast_errorstarted_atfinished_atresult_versionupdated_at
建议唯一约束:
unique(job_id, domain_id)
说明:
- 多台 Worker 只抢
status=pending且未被占用或租约已过期的任务 claim_token用于避免状态回写串单lease_expires_at用于机器故障后的任务回收
4. detect_worker_nodes
表示 Worker 节点。
建议字段:
node_coderegionrolehostnameipstatusworker_versionlast_heartbeat_atcurrent_loadremark
说明:
- 后台后续可以直接展示“哪个节点在线、哪个节点在跑、当前负载多少”
5. detect_run_events
表示高价值阶段日志。
建议字段:
idjob_idjob_item_idnode_codeevent_typelevelmessagepayload_jsoncreated_at
说明:
- 页面展示优先依赖事件流,不直接硬依赖某一台机器本地日志文件
- 原始大日志仍保留在大陆 Worker 本地
七、多机抢任务与去重
1. 抢任务方式
推荐使用数据库租约模型:
- Worker 从
detect_job_items中领取一批待处理任务 - 使用
FOR UPDATE SKIP LOCKED或原子更新方式抢占 - 领取后写入:
claimed_byclaim_tokenlease_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. 国外机器
目录建议:
/opt/domaincheck
├── domain-api
├── domain-web
└── shared
部署角色:
webapipostgresql_main
2. 大陆调度中心
目录建议:
/opt/domaincheck
├── domainCheck
├── runtime-db
├── redis
└── shared
部署角色:
schedulerredispostgresql_runtimeworker
3. 大陆纯 Worker 节点
目录建议:
/opt/domaincheck
├── domainCheck
└── shared
部署角色:
worker
4. 一键化原则
部署脚本必须满足:
- 新机器只需要少量环境变量
- 不要求现场手工改很多路径
- 国外机和大陆机各自有独立入口脚本
- 后续新增大陆 Worker 节点继续复用同一脚本
5. 当前建议的最小部署
当前最适合正式起步的形态:
- 国外
1台:domain-webdomain-api- 国外主 PostgreSQL
- 大陆
1台:redis- 大陆运行库 PostgreSQL
detect-worker- 后续可补
sync-service
扩容时优先:
- 新增大陆 Worker
- 再视压力拆分大陆运行库与 Redis
- 国外控制面保持稳定,不跟着高频扩缩
十、阶段性落地建议
第一阶段
先完成:
- 国外控制面部署
- 大陆单机执行面部署
- 节点注册与心跳
- 当前后台继续可用
第二阶段
再完成:
- 任务项模型
- 多 Worker 抢任务
- 事件日志流
- 结果异步同步回国外
第三阶段
最后完成:
- 大陆多 Worker 横向扩容
- 运行库与调度中心独立拆分
- 国外归档和监控补齐
十一、运行与观测建议
多机之后,不能再只靠“登录某台机器 tail 日志”来判断系统状态。
建议分层:
- 大陆 Worker 本地保留完整原始日志
detect_run_events保存关键阶段事件- 国外后台优先展示事件流、节点负载和同步状态
- 真排障时再下钻到具体节点原始日志
后台建议优先展示:
- 当前在线节点数
- 每节点最近心跳
- 每节点当前负载
- 当前活跃任务数
- 队列积压与最老待领年龄
- 租约是否过期、是否即将到期
- 近 15 分钟每节点吞吐
- 最近失败摘要
- 最近同步结果
- 最近结果摘要投影
十二、下一阶段开发清单
接下来建议按下面顺序继续落地:
- 后台创建
detect_jobs / detect_job_items - Worker 实现批量 claim / renew / finish / fail
- 增加租约超时回收
- 将检测阶段事件写入
detect_run_events - 增加
sync-service - 完成第二台大陆 Worker 接入演练
十三、最终建议
对当前项目来说,最优解不是“全部放国外”,也不是“全部放大陆”,而是:
- 国外做控制面和主结果库
- 大陆做执行面和运行态调度
- 先从
国外 1 台 + 大陆 1 台起步 - 后续优先横向增加大陆 Worker
一句话总结:
先把职责边界设计对,再让部署简单;后续扩容时只加 Worker,不再重构核心架构。