这一页为什么不在 crs.tongsou.com 上
CONTRACT.md §一 · CONVENTION.md 第三条 · DEV-sampler.md §一
⚠️ 你列的九列里,只有采样器能提供其中一部分。下载器没有读 sampler:domain_rule 的边,把这张表放在 crs.tongsou.com 上,第一件事就是新增一条契约里不存在的边 —— 「跨服务只能通过 CONTRACT.md 里已定义的边传递,没有边就是不能传」。
⚠️ 采样器也不读写调度器的任何表,尤其不碰 scheduler:domain_health_runtime。所以「可用性状态」「熔断原因」「URL 数量」这几列在本页同样拿不到,见下面第四节,不是漏做。
⚠️ 下载器侧的域名视角另有一页(domain-runtime.html,只管限速:last_request_at、in_flight_count)。两页看的是两个独立计数器,故意重复,不同步,数字对不上是设计如此。
先拆两个词:「状态」和「原因」
CONVENTION.md 第二条与第四条 · 同名不同义是本系统反复在防的事故
你说的「状态」在文档里是四个不同的枚举,「原因」是四个不同的字段。合成一列就会静默走错分支 —— JSON 反序列化不会报错,只会走错。所以主表里各拆成独立的列。
四个「状态」
| field字段 | owner所有者 | level粒度 | values取值 |
|---|---|---|---|
| verdict判定结论 | 采样器 | 域名 | csr / partial / ssr / unknown |
| domain_health_status域名可用性熔断状态 | 调度器 | 域名 | serving / suspended / probing_availability / probing_content |
| schedule_status调度状态机 | 调度器 | URL | ready / dispatched / done / failed |
| fetch_status抓取结果状态 | 下载器 | URL | done / empty / not_modified / failed |
四个「原因」
| field字段 | owner所有者 | level粒度 | meaning含义 |
|---|---|---|---|
| undetermined_reason判不出来的原因 | 采样器 | 域名 | 这个域名为什么判不出来 |
| verdict_reason判定结论说明 | 采样器 | 域名 | 结论的自由文本说明 |
| suspend_reason熔断原因 | 调度器 | 域名 | 这个域名为什么被熔断 |
| fail_reason失败原因 | 下载器 | URL,单次 | 这一次抓取为什么失败 |
⚠️ undetermined_reason 与 suspend_reason 的取值集不得有交集。前者是「判不出来是不是 CSR」,后者是「现在能不能连上」,语义不同,不许互相赋值。部分重叠比完全不同更危险 —— 会让人以为可以互相赋值。
⚠️ 全系统只允许四个「为什么失败」类字段,新增需三方确认。前端不得自造第五个。
全库域名
sampler:domain_rule:{domain} · 唯一写入方 采样器 · 表头格式:field name(中文名,文档里没给中文名的不硬加)
- domain_rule 合计
- 134,706
- verdict = csr
- 108,412 80.5%
- verdict = partial
- 14,318 10.6%
- verdict = ssr
- 8,204 6.1%
- verdict = unknown
- 3,772 2.8%
- judgement_task 进行中
- 1,204
| domain域名 | sitemapsitemap 地址 · 无具名字段 | verdict判定结论 | undetermined_reason判不出来的原因 | URL countURL 数量 · 不可得 | first_submitted_at首次提交时间 | verified_at判定确认时间 · 复核到期依据 | last_attempted_at最后一次判定尝试时间 | actions操作 |
|---|---|---|---|---|---|---|---|---|
| news.example.tw | https://news.example.tw/sitemap.xml | csr | — | — | 2026-07-02T03:11:20Z | 2026-08-11T09:20:04Z | 2026-08-11T09:19:41Z | 规则 · 复核 |
| docs.example.cn | https://docs.example.cn/sitemap_index.xml | csr | — | — | 2026-06-18T22:40:07Z | 2026-08-09T14:02:55Z | 2026-08-09T14:02:31Z | 规则 · 复核 |
| shop.example.jp | — | partial | — | — | 2026-07-25T11:06:38Z | 2026-08-13T05:47:12Z | 2026-08-13T05:46:50Z | 规则 · 复核 |
| corp.example.hk | https://corp.example.hk/sitemap.xml | ssr | — | — | 2026-05-30T08:15:02Z | 2026-08-02T19:31:44Z | 2026-08-02T19:31:20Z | 规则 · 复核 |
| portal.example.mo | — | unknown | judgement_timeout | — | 2026-08-04T01:22:19Z | 2026-08-14T23:10:06Z | 2026-08-14T23:09:38Z | 规则 · 待定 |
| members.example.com | — | unknown | requires_authentication | — | 2026-07-11T16:44:53Z | 2026-08-12T07:55:29Z | 2026-08-12T07:54:58Z | 规则 · 待定 |
| gone.example.net | — | unknown | domain_not_resolvable | — | 2026-06-09T04:30:11Z | 2026-08-08T02:18:47Z | 2026-08-08T02:18:47Z | 规则 · 待定 |
| blocked.example.eu | https://blocked.example.eu/sitemap.xml | unknown | robots_disallowed | — | 2026-07-29T13:02:40Z | 2026-08-10T11:26:03Z | 2026-08-10T11:25:37Z | 规则 · 待定 |
⚠️ verdict = ssr 与 verdict = unknown 的域名照常写 sampler:domain_rule,所以它们出现在本表里,不是脏数据。不写表,同一域名每被链接一次就真烧 3~5 次渲染 —— 一个被上万页面链接的失败域名代价极高。
⚠️ partial 不是「弱一点的 ssr」,是分歧极大、最不该静默丢的那批,调度器会入队并降优先级。verdict 是枚举不是布尔,布尔会让 partial 坍缩成 falsy 被当 SSR 丢弃。
⚠️ undetermined_reason 只在 verdict = unknown 时有值,取值只能取 DEV-sampler.md §8.4 的九个之一,前端不得自造。verdict = unknown 还有两种来源语义(新域名判定失败 / 复核失败),动作不同,详情页要能区分。
拿不到的四列,以及要怎样才能拿到
这不是漏做 —— 每一行缺的都是一条契约里不存在的边,或一个文档里不存在的字段
| column你要的列 | owner数据在谁那里 | what is missing缺什么 | how to get it怎样才能有 |
|---|---|---|---|
| sitemap 地址 | 采样器 | 没有具名字段。sampler:robots_rule 只存 content / fetched_at / crawl_delay / disallow_prefix_list,sitemap 地址藏在 content 的 Sitemap 指令里,没被单独取出来 |
在 sampler:robots_rule 加一个字段。属采样器内部表,不动契约,但要过 CONVENTION 附录 A 六问:数组要带 _list 后缀,名字不能与网页对象的 robots_snapshot 混 |
| 状态(可用性侧) | 调度器 | domain_health_status 与 suspend_reason 在 scheduler:domain_health_runtime,唯一写入方是调度器,采样器明令不碰 |
放到调度器的同名页去看,不要跨过来。熔断与判定完全解耦是刻意设计:接通了就要定义跨服务失败语义,而且两边会互相驱动 |
| URL 数量 | 调度器 | 两个都缺:没有边,也没有字段。调度器侧只有配额上限 max_url_count_per_domain,没有「该域名当前有多少 URL」的具名字段 |
先由 DEV-scheduler.md 定这个字段该不该有、叫什么,再谈接口。它既不是 page_sample_count 也不是 statistic_sample_count,那两个已经因为合并出过一次事故 |
| 更新周期 | 调度器 / 采样器 | 没有具名字段。复核靠 verified_at 到期,重抓靠 URL 级的 next_fetch_at,待定域名靠 30 / 90 / 180 天退避 —— 三个都是机制,都不是一个叫「周期」的字段 |
抓取调度分层(实时 / 高频 / 常规 / 低频 / 冻结五档)第一版不做,见 DEV-scheduler.md §十二。做了之后 next_fetch_at 已能承载,不需改 schema |
| 最后抓取时间 | 三个服务各有一个 | 本表给的是 last_attempted_at(最后一次判定采样尝试)。另外三个是:downloader:domain_runtime.last_request_at、网页对象的 fetched_at(URL 级)、scheduler:domain_health_runtime.last_success_at |
不要合成一列。四个的含义、粒度、写入方都不同;last_success_at 从未成功过的域名是 null,不是零值时间 |
⚠️ 上表第三行是最值得先解决的一条:max_url_count_per_domain 是配额,不是计数。域名级配额本来就挡不住爬虫陷阱 —— 陷阱 URL 在浅层密集出现会先把配额吃光,真正有价值的新页面反而进不来。要看「URL 数量」,得先有按 URL 模式分别配额的那一版。
操作:只提供契约里已有的边
CONTRACT.md §四 · 域名级配置只能有一个所有者
| action操作 | endpoint对应接口 | note说明 |
|---|---|---|
| 查看规则 | GET sampling-url/v1/rules/{domain} | 批量用 POST /v1/rules/batch,调度器冷启动走的也是这条 |
| 提交判定 | POST sampling-url/v1/domains | reason = manual。异步立即返回 accepted_count 与 task_ids |
| 提交复核 | POST sampling-url/v1/domains | reason = recheck。人工复核走这条,不走 /v1/recheck —— 后者的 signal 四个取值全部由调度器的统计发出,控制台不伪造 signal |
| 观察待定池 | GET sampling-url/v1/undetermined-domains | 只读观察。不产生认领,不改变任何状态 |
| 投种子 | POST scheduling-url/v1/seeds | 调度器接口,从本页跳转过去操作,不由采样器代发 |
故意不提供的操作
| not offered不提供 | why为什么 |
|---|---|
| 直接改 verdict | 域名级配置只能有一个所有者。外部只能提交建议(hint),采样器确认后才写规则表 —— 与 v2 的 /v1/api-endpoint-hints 同一模式 |
| 暂停 / 恢复域名、解除熔断 | scheduler:domain_health_runtime 唯一写入方是调度器。恢复必须走两段式探针(先 raw 再 render),绕过去会在「网关起了后端没起」时全量放开抓空壳 —— 一万条 URL 抓回来全是空壳 |
| 缩短 throttle_until_at | 严禁缩短。严格遵守对方给的 Retry-After,不打折,这是「不把对方干崩」的唯一硬承诺 |
| 转人工 | 早期设计里 unknown 的终点就是「转人工」,那是一个伪装成流程的永久拉黑,比明确拉黑更糟 —— 它制造了「有人会处理」的错觉。已废除,出口是 30 / 90 / 180 天退避重采加外部认领 |
| 永久拉黑域名 | 451 都不做永久拉黑,走 30 天探针。探针不设次数上限,封顶后永久按最低频探下去 |
| 删除域名规则 | ssr 与 unknown 必须留在表里。删了,同一域名每被链接一次就重新采样,真烧 3~5 次渲染 |
| 手工调小 min_interval_ms | 采样器已取 max(crawl_delay, 语种默认值),下游禁止再取一次 max。调小等于绕过对方 robots 里的 crawl_delay |
⚠️ 本页的「观察」不构成认领。纯 GET 查询接口不能替代认领 —— 查询没有「谁在处理」的概念,两个外部模块会同时抓同一个域名,限速的单点保证就失效了。认领走 POST /v1/undetermined-domain-claims,且认领期间采样器停止对该域名的一切请求。
术语对照
只列本页出现的字段 · 取值定义在 DEV 文档,本表不抄取值
| field字段 | 中文名 | owner所有者 | ⚠️ 易混提示 |
|---|---|---|---|
| domain | 域名 | 三服务共用 key | |
| verdict | 判定结论 | 采样器 | 枚举不是布尔。取值见 DEV-sampler.md §四 |
| verdict_reason | 判定结论说明 | 采样器 | 自由文本,不是枚举 |
| undetermined_reason | 判不出来的原因 | 采样器 | 与 suspend_reason 取值集无交集。取值见 §8.4 |
| suspend_reason | 熔断原因 | 调度器 | 本页不可得。取值见 DEV-scheduler.md §9.6 |
| domain_health_status | 域名可用性熔断状态 | 调度器 | 本页不可得。刻意不用 open / closed / half_open —— 标准熔断器术语里 open 表示「断开 = 停止」,与中文「开着 = 能用」完全相反 |
| first_submitted_at | 首次提交时间 | 采样器 / 调度器同名同义 | 动词一律用过去分词,且同一张表内时态用词必须一致(first_ 与 last_ 成对) |
| last_attempted_at | 最后一次判定尝试时间 | 采样器 | 不是最后抓取时间。见上一节第五行的四个时间字段 |
| verified_at | 判定确认时间 | 采样器 | 幂等键的一半(domain + verified_at)。必须单调,收到更旧的值丢弃并返回 200 |
| page_sample_count | 每域名采样页数 | 采样器 | 不是 statistic_sample_count(调度器的滑动中位数样本数)。两者曾被合并成一个 sample_count,是本系统要防的事故本身 |
| judgement_attempt_count | 判定尝试次数 | 采样器 | |
| min_interval_ms | 最小请求间隔(毫秒) | 采样器算出,全系统同名 | 已取过 max,下游禁止再取一次 max。单位必须在名字里 |
| wait_selector | 文档未给中文名 | 采样器 | 采样器最有价值的产出,下载器等待策略里排第一。推不出就留空 |
| list_link_ratio | 文档未给中文名 | 采样器 | 只取自列表页样本,与正文无关。极低说明该域名列表页链接靠 JS 注入 |
| disallow_prefix_list | 文档未给中文名 | 采样器解析,调度器执行 | 下载器不开 --obey-robots,执行者只有调度器。这一环断了,全系统没人遵守 robots |
| robots_rule_content | 文档未给中文名 | 采样器 | 导出字段,来自会刷新的 sampler:robots_rule。不是网页对象里那份抓取时刻的同期证据 robots_snapshot,两者不可互相替代,名字也不可互借 |
⚠️ 四个字段的「中文名」一栏写着文档未给中文名,那里就空着,不硬造。硬造一个中文名,下一次讨论里它就会变成这个字段的第二个名字 —— 命名漂移不是「难看」的问题,status 在两个服务里是两套不同枚举,JSON 反序列化不会报错,只会静默走错分支。
⚠️ 本表只写「这里有个枚举、别和另一个搞混」,不抄取值。取值属于语义,归 DEV 文档;抄一份到这里,两边就会漂。