怎么读这三份源码 · 不迷路的方法

三份源码加起来一万七千多个文件,通读是不可能的。这一章给一套可操作的读法:先读仓库自带的文档(它们都带 AGENTS.md 和架构文档)、从 cmd/ 找入口、盯住 Go 的接口、拿测试当用法手册,以及顺着数据流而不是目录树读。

作者 David更新于 第 5 篇(共 52 篇)

怎么读这三份源码 · 不迷路的方法

三个主角加起来一万七千多个 Go/TS 文件。通读是不可能的,也没必要。这一章给一套可操作的读法——本专栏自己就是按这套方法写的。

第一条:先读仓库自带的文档

最省力的一步,却最常被跳过:这三个仓库都自带了高质量的架构文档。

读大型项目源码,第一件事永远是“找作者留下的地图”,而不是一头扎进代码。 这三份都有:

  • AGENTS.md / CLAUDE.md(三个仓库都有!) —— 这是个很“当下”的现象:它们是写给 AI 编码助手看的约定文件。但对人类同样有用——它们浓缩了维护者的偏好和项目惯例。比如 Prometheus 的 AGENTS.md 列出了 PR 标题必须用的 area 前缀:tsdb、tsdb/wlog、promql、discovery/<name>、agent、alerting、textparse、ui……这张前缀表本身就是一份子系统地图,告诉你这个项目是由哪些模块构成的。
  • tsdb/docs/format/(Prometheus) —— 磁盘格式的官方规范:index.md、chunks.md、wal.md、head_chunks.md、tombstones.md。还有 tsdb/docs/bstream.md 讲位流。第 2 部分基本就是围着这几份规范 + 对应实现读的。
  • docs/internal-architecture.md(OTel Collector) —— 直接给了启动流程图和“该从哪几个文件开始读”的清单:otelcol/collector.go、service/internal/graph/graph.go、component/component.go。
  • tsdb/README.md —— 甚至给了原始设计文章和 Gorilla 论文的链接。

官方文档 + 源码对读,比任何二手博客都可靠——博客会过时,tsdb/docs/format/ 和它旁边的代码是一起演进的。

第二条:从 cmd/ 找入口,顺着数据流读

Go 项目有个好处:入口非常明确。

拿到入口后,顺着数据流读,而不是按目录树顺序读:

图表加载中…

那条“指标的一生”就是你的阅读顺序:抓取 → 存储 → 查询 → 告警。按数据流读,每一步都有“接下来它去哪”的悬念带着你,不容易迷路;按目录字母序读,读到第三个包就忘了前面在干嘛。

第三条:Go 的接口是骨架

读 Go 项目,接口(interface)是最重要的路标——它们定义了系统的关节:

接口在哪是什么关节
storage.AppenderPrometheus storage/抓取怎么把数据交给存储
storage.QuerierPrometheus storage/查询怎么从存储读数据
consumer.ConsumerCollector consumer/管道组件间怎么传数据(背压在这)
component.ComponentCollector component/所有组件的生命周期

找到接口,就找到了“可替换的边界”——而边界处往往就是设计的精华。 比如 Prometheus 的 storage.Appender:抓取模块只认这个接口,不关心背后是 TSDB 还是远程写还是测试用的假实现。看懂这个接口,你就懂了“采集”和“存储”是怎么解耦的。

Collector 更是把这点做到极致:五类组件全都是接口 + Factory,整个系统就是“一堆接口 + 一个把它们连起来的图”。所以读 Collector,读懂 component/、consumer/、service/internal/graph/ 这三处,主体就通了——剩下的具体 receiver/exporter 都是同一模式的复制。

第四条:测试是最好的用法手册

Go 的 _test.go 就在被测代码旁边,这是巨大的便利:

  • 想知道某个类型怎么用 → 看它的 _test.go,里面是可运行的最小示例。
  • 想知道边界行为 → 测试里通常列全了各种边界 case。
  • Prometheus 还有 promql/promqltest/ —— 一套声明式的 PromQL 测试框架,用文本描述“给这些数据、跑这个查询、该得这个结果”。读 PromQL 语义时,这些测试文件比文档还清楚。
  • example_test.go(如 tsdb/example_test.go)—— Go 的惯例,专门写给人看的示例。

第五条:接受“读不完”,按需下钻

最重要的心态:不要试图读完。 Grafana 有 1.5 万个文件,其中绝大部分是 UI 组件和业务代码——读完既不可能也无价值。正确的做法是:

  • 主干读透,枝叶跳过 —— TSDB 的压缩和索引值得逐行读;某个具体数据源的适配代码扫一眼知道套路即可。
  • 带着问题读 —— “rate() 为什么会外推?”比“我要读完 promql 包”有效一万倍。问题给你路径和终点。
  • 允许有黑盒 —— 读 TSDB 时,先把压缩算法当黑盒(“它能把点压小”),把主流程走通,再回头钻进去。一次只开一个黑盒。

本专栏就是这么组织的:每一章一个具体问题,顺着数据流推进,主干处下钻到字节级(如磁盘格式),枝叶处点到为止。

本专栏的写作约定

最后交代一下,方便你对照源码验证:

  • 给真实坐标 —— 凡有结论,尽量给真实的文件路径 + 类型名 + 关键函数(如 tsdb/head.go 的 Head.Appender),你可以直接去仓库里核对。
  • 代码片段是节选 —— 为了可读,会省略次要分支(标 ...)、简化错误处理,但不虚构:签名和关键逻辑忠于源码。
  • 版本明确 —— Prometheus 3.13.1 / Collector 0.156.0 / Grafana 13.1.0。这些项目在快速演进,本专栏是某个时刻的切片;读的时候留意你手上的版本可能已经不同。

小结与下一部分

读这三份源码的方法:① 先读仓库自带的文档——三者都有 AGENTS.md/CLAUDE.md(维护者约定,Prometheus 的 area 前缀表就是子系统地图)、tsdb/docs/format/(磁盘格式规范)、docs/internal-architecture.md(启动流程 + 该读哪些文件);② 从 cmd/ 找入口、顺数据流读而非按目录序;③ 盯住 Go 接口(storage.Appender/consumer.Consumer 就是系统关节和可替换边界);④ 拿 _test.go 当用法手册(promqltest 尤佳);⑤ 接受读不完,带着问题、主干读透枝叶跳过。

第 0 部分到此结束——你已经知道可观测性是什么、三大支柱、数据的一生、三个主角和读法。下一部分开始动真格,从 Prometheus 的数据模型与采集入手。👉 第 1 部分 · Prometheus:数据模型与采集。

参考资料

  • [1] Prometheus 源码 AGENTS.md(area 前缀与提交约定)、tsdb/README.md、tsdb/docs/format/、tsdb/docs/bstream.md。
  • [2] OpenTelemetry Collector 源码 docs/internal-architecture.md(启动流程图与入门文件清单)。
  • [3] 关联三个主角、一条指标的一生。

本页目录