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

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

- 作者：David（道雾轩）
- 专栏：指标采集与观测（https://daiw.org/manual/observability.md）
- 最后更新：2026-07-15
- 原文：https://daiw.org/manual/observability/reading-the-source
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

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

[三个主角](https://daiw.org/manual/observability/the-three-projects)加起来一万七千多个 Go/TS 文件。**通读是不可能的**，也没必要。这一章给一套**可操作的读法**——本专栏自己就是按这套方法写的。

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

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

<Callout type="info">
  **读大型项目源码，第一件事永远是“找作者留下的地图”，而不是一头扎进代码。** 这三份都有：

  - **`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 部分](https://daiw.org/manual/observability/data-journey)基本就是围着这几份规范 + 对应实现读的。
  - **`docs/internal-architecture.md`(OTel Collector)** —— 直接给了**启动流程图**和“该从哪几个文件开始读”的清单：`otelcol/collector.go`、`service/internal/graph/graph.go`、`component/component.go`。
  - **`tsdb/README.md`** —— 甚至给了原始设计文章和 Gorilla 论文的链接。

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

## 第二条：从 cmd/ 找入口，顺着数据流读

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

- **`cmd/prometheus/main.go`** —— Prometheus 的 `main`。从这里能看到它怎么把[服务发现、抓取、TSDB、查询引擎、规则引擎](https://daiw.org/manual/observability/data-journey)这些组件拼起来。
- **`otelcol/collector.go`** —— Collector 的核心（[它的架构文档直接点名](https://daiw.org/manual/observability/reading-the-source)）。

拿到入口后，**顺着数据流读，而不是按目录树顺序读**：

```mermaid
flowchart LR
    A["找入口<br/>cmd/*/main.go"] --> B["找数据流的第一站<br/>(如 scrape)"]
    B --> C["跟着数据走<br/>scrape → storage → tsdb"]
    C --> D["遇到接口就停下<br/>看它有哪些实现"]
    D --> E["需要细节时<br/>再翻对应的 docs/"]
```

**[那条“指标的一生”](https://daiw.org/manual/observability/data-journey)就是你的阅读顺序**：抓取 → 存储 → 查询 → 告警。按数据流读，每一步都有“接下来它去哪”的悬念带着你，不容易迷路；按目录字母序读，读到第三个包就忘了前面在干嘛。

## 第三条：Go 的接口是骨架

读 Go 项目，**接口（interface）是最重要的路标**——它们定义了系统的关节：

| 接口 | 在哪 | 是什么关节 |
| --- | --- | --- |
| `storage.Appender` | Prometheus `storage/` | [抓取](https://daiw.org/manual/observability/data-journey)怎么把数据交给[存储](https://daiw.org/manual/observability/data-journey) |
| `storage.Querier` | Prometheus `storage/` | [查询](https://daiw.org/manual/observability/data-journey)怎么从存储读数据 |
| `consumer.Consumer` | Collector `consumer/` | [管道组件间](https://daiw.org/manual/observability/the-three-projects)怎么传数据（**背压在这**） |
| `component.Component` | Collector `component/` | 所有[组件的生命周期](https://daiw.org/manual/observability/the-three-projects) |

<Callout type="info">
  **找到接口，就找到了“可替换的边界”——而边界处往往就是设计的精华。** 比如 Prometheus 的 `storage.Appender`：[抓取模块](https://daiw.org/manual/observability/data-journey)只认这个接口，不关心背后是 [TSDB](https://daiw.org/manual/observability/data-journey) 还是[远程写](https://daiw.org/manual/observability/data-journey)还是测试用的假实现。看懂这个接口，你就懂了“[采集](https://daiw.org/manual/observability/data-journey)”和“[存储](https://daiw.org/manual/observability/data-journey)”是怎么解耦的。

  Collector 更是把这点做到极致：[五类组件](https://daiw.org/manual/observability/the-three-projects)全都是接口 + `Factory`，**整个系统就是“一堆接口 + 一个把它们连起来的图”**。所以读 Collector，读懂 `component/`、`consumer/`、`service/internal/graph/` 这三处，主体就通了——剩下的具体 receiver/exporter 都是同一模式的复制。
</Callout>

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

Go 的 `_test.go` 就在被测代码旁边，这是巨大的便利：

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

## 第五条：接受“读不完”，按需下钻

<Callout type="warn">
  **最重要的心态：**不要试图读完**。** Grafana 有 1.5 万个文件，其中绝大部分是 UI 组件和业务代码——**读完既不可能也无价值**。正确的做法是：

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

  本专栏就是这么组织的：**每一章一个具体问题**，顺着[数据流](https://daiw.org/manual/observability/data-journey)推进，主干处下钻到字节级（如[磁盘格式](https://daiw.org/manual/observability/data-journey)），枝叶处点到为止。
</Callout>

## 本专栏的写作约定

最后交代一下，方便你对照源码验证：

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

## 小结与下一部分

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

第 0 部分到此结束——你已经知道[可观测性是什么](https://daiw.org/manual/observability/why-observability)、[三大支柱](https://daiw.org/manual/observability/three-pillars)、[数据的一生](https://daiw.org/manual/observability/data-journey)、[三个主角](https://daiw.org/manual/observability/the-three-projects)和读法。下一部分开始动真格，从 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] 关联[三个主角](https://daiw.org/manual/observability/the-three-projects)、[一条指标的一生](https://daiw.org/manual/observability/data-journey)。
