上一篇讲了 YARN HA 和 Federation。本篇讲 YARN Timeline Service v2——YARN 提供的通用应用历史与指标系统,是第二阶段的最后一篇。

Timeline Service 常被介绍成"YARN 的作业历史记录"。这个描述对应了功能但漏了 v2 与 v1 的架构差异。准确的说法是:Timeline Service v2 是 Hadoop 3.0 引入的下一代架构,与 v1 的"集中式 Timeline Server"不同,v2 把写入侧改造成"每个 AM / NM 内嵌 TimelineCollector",把读侧改造成"无状态 Reader + 可扩展 HBase 后端",让 Timeline 系统可以横向扩展支撑万节点集群。

本篇只抓一个问题:Timeline Service v2 解决了 v1 的什么瓶颈、Per-App Collector 和 TimelineReader 各自怎么工作、为什么 v2 的后端必须用 HBase 而不是直接用 HDFS。

v1 的设计缺陷

Timeline Service v1(Hadoop 2.4 引入)的设计是"集中式 Timeline Server":

1
所有 AM / NM ──► Timeline Server(单进程)──► HDFS / HBase

这个设计在小集群(几百节点)工作良好,但在大集群暴露了三个结构性问题:

第一,写入瓶颈。集群里所有 AM 和 NM 都把事件发到单一 Timeline Server,Timeline Server 进程的网卡和 CPU 成为瓶颈。5000 节点集群上每秒可能有几十万事件(每个 task 几十个事件 × 数千并发 task),单 Timeline Server 处理不过来。

第二,写入与计算耦合。Timeline Server 是一个独立 JVM 进程,但它要部署在哪里?部署在专用节点(资源浪费)?部署在某个 NM 节点上(与计算争资源)?这个部署难题在 v1 时代没有好的解。

第三,数据模型不区分。v1 把"通用应用元数据"(作业 ID、提交用户、队列、状态)和"框架特定数据"(MapReduce counter、Spark stage 信息、Tez DAG)混在一起,没有清晰的 schema 区分。这让 Reader 查询时很难按维度过滤,性能也差。

v2 的架构改造

Timeline Service v2(Hadoop 3.0 引入)针对 v1 的三个问题做了架构改造:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
写入侧改造:
- 每个 AM 内嵌 Per-App TimelineCollector(事件聚合器)
- 每个 NM 内嵌 NM TimelineCollector(container 事件聚合)
- Collector 在本地内存攒批,批量写到后端
- 写入负载分散到所有 AM / NM,不再集中在单 Timeline Server

读取侧改造:
- TimelineReader 是无状态服务,可以部署多个实例做负载均衡
- Reader 只接受 REST 请求,转发到 HBase
- 读负载通过 Reader 横向扩展,不再受单 Server 限制

后端改造:
- 强制使用 HBase 作为后端
- schema 按"flow run + app + entity"三级聚合,支持跨 app 查询
- HBase 的 LSM-Tree + RegionServer 让写入吞吐高、按 rowkey 查询快

这种"分布式写入 + 无状态读取 + LSM-Tree 后端"的组合让 Timeline v2 可以扩展到万节点集群。每秒几十万事件被分散到所有 AM / NM 的 Collector,批量写 HBase,吞吐线性扩展。

为什么后端必须用 HBase 而不是 HDFS?因为 Timeline 数据的访问模式是"按 app-id / entity-type / time-range 查询",不是顺序扫描。HDFS 适合大文件顺序读,按 key 查询需要 NameNode 元数据 + DataNode 随机读,性能差。HBase 的 LSM-Tree + RegionServer 天然支持按 rowkey 高效查询,是 Timeline 系统的正确后端选择。

Per-App TimelineCollector

v2 的核心创新是 Per-App TimelineCollector。每个 AM 启动时在自己的 JVM 里内嵌一个 Collector 实例,AM 内部产生的事件(task 启动、task 完成、counter 更新)通过本地方法调用发给 Collector,不经过网络。

1
2
3
4
5
6
7
8
9
AM 进程:
├─ ApplicationMaster 主逻辑
├─ Per-App TimelineCollector(本地内存攒批)
└─ 其他组件

NM 进程:
├─ NodeManager 主逻辑
├─ NM TimelineCollector(container 事件攒批)
└─ 其他组件

Collector 在本地内存里维护一个环形 buffer 或 list,攒够一定数量事件或达到时间窗口后批量写到 HBase。这种"本地攒批 + 批量写"模式让网络 RPC 减少 100 倍以上——单个事件可能几百字节,攒成 1000 个事件的批量后一次 RPC 写 1 MB。

Per-App Collector 还带来一个隐性收益——写入隔离。一个作业产生大量事件不会拖慢其他作业的写入,因为每个作业有自己的 Collector 和本地 buffer。v1 时代所有作业挤一个 Timeline Server,一个"日志狂"作业可以拖慢整个集群的 Timeline 写入。

NM 上的 TimelineCollector 处理 container 级事件——container 启动、container 完成、container 资源使用指标。这些事件由 NM 自己产生,攒批后写 HBase。

数据模型:Flow / Run / App / Entity

v2 的 schema 设计是 Timeline v2 与 v1 的另一个关键差异。v2 引入了"Flow → FlowRun → App → Entity"四级聚合:

1
2
3
4
Flow:一组逻辑相关的作业(例如"每日 ETL 流"包含的多个作业)
└─ FlowRun:一次 Flow 执行(例如"2026-07-26 的每日 ETL")
└─ App:FlowRun 内的一个 YARN 作业
└─ Entity:作业内的某个对象(task、attempt、counter)

这种层级让 Timeline 数据可以按维度查询:

1
2
3
4
查询1:列出 flow "DailyETL" 的最近 7 天所有 run
查询2:查询 flow run "DailyETL/2026-07-26" 内所有 app 的状态
查询3:查询 app "application_1721900000_0001" 的所有 task entity
查询4:查询某个 task 的 counter 历史

v1 时代这种跨 app 查询几乎不可能——v1 只有 app 和 entity 两级,没有 flow / run 的概念。v2 的四级聚合让"作业编排视角"成为一等公民——用户可以从"这次 ETL 流怎么样"开始查询,而不是从"某个作业的某个 task"开始。

HBase 的 rowkey 设计按 flow / run / app / entity 复合,让按 flow 维度的查询高效(同一 flow 的数据在 HBase 上连续存放,scan 性能好)。

Reader 与 REST API

TimelineReader 是 v2 的读取侧组件,提供 REST API 给 UI 和客户端:

1
2
3
4
GET /ws/v2/timeline/apps/{appId}/entities/{entityType}
GET /ws/v2/timeline/clusters/{clusterId}/flows/{flowName}/runs/{runId}/apps/{appId}
GET /ws/v2/timeline/clusters/{clusterId}/flows/{flowName}/runs/{runId}
GET /ws/v2/timeline/clusters/{clusterId}/flows/{flowName}

这套 REST API 支持 flow / run / app / entity 四个层级的查询,支持按时间范围、按 entity type、按 attribute 过滤。

Reader 是无状态服务——它只是 HBase 的查询代理。可以部署多个 Reader 实例做负载均衡,任何一个 Reader 挂掉客户端切换到另一个。这种无状态特性让 Reader 可以放在任意节点(包括专用的 Timeline Service 节点或者 NM 节点上同部署)。

Hadoop 自带的 YARN Timeline Web UI(在 ResourceManager Web UI 的 “Timeline service” 标签下)通过 Reader REST API 渲染作业历史、task 详情、counter 等。第三方 UI(Apache Zeppelin、Ranger Audit UI)也可以用同一套 REST API。

与计算框架的集成

Timeline v2 的价值依赖计算框架主动上报事件。Hadoop 3.x 默认集成了三个框架:

MapReduce(MRv2):MRAppMaster 内置 TimelineClient,把 task 启动、task 完成、counter 等事件自动发给 Per-App Collector。用户不需要修改作业代码。

Tez:Tez DAG AppMaster 同样内置 TimelineClient,把 DAG 节点执行事件发给 Collector。Tez UI 通过 Reader REST API 渲染 DAG 视图。

Spark:Spark AppMaster 配置 spark.hadoop.yarn.timeline-service.enabled=true 后会把 stage / task 事件发给 Collector。Spark History Server 可以读 Timeline 数据。

Flink:Flink JobManager 通过 TimelineClient 把 job / task 事件发给 Collector。Flink Web UI 可以集成 Timeline 数据。

其他自定义 AM 框架需要实现 TimelineClient 接口主动上报事件。这是 v2 的额外开发成本——计算框架需要主动集成。不集成的话 Timeline v2 仍然能记录 NM 层面的 container 事件(container 启动 / 完成、资源使用指标),但 AM 层面的事件(task 进度、counter)就缺失。

实验:观察 Timeline Service

部署 Timeline v2 的集群,配置 yarn.timeline-service.enabled=trueyarn.timeline-service.version=v2。提交一个 MapReduce 作业,用 REST API 查询作业事件:

1
2
3
4
5
6
7
8
# 查询某作业的所有 entity
curl http://timeline-reader-host:8198/ws/v2/timeline/apps/application_1721900000_0001/entities/YARN_APPLICATION

# 查询某 flow 的所有 run
curl http://timeline-reader-host:8198/ws/v2/timeline/clusters/mycluster/flows/DailyETL

# 查询某 run 内的所有 app
curl http://timeline-reader-host:8198/ws/v2/timeline/clusters/mycluster/flows/DailyETL/runs/run_20260726

输出 JSON 格式的事件列表,每个事件包含 entity type、timestamp、attributes 等。

HBase 后端可以直接查询数据:

1
2
3
hbase shell
> list 'timelineservice.*'
> scan 'timelineservice.app_flow_table', {LIMIT => 5}

可以看到 Timeline 数据按 rowkey(cluster + flow + run + app 复合)存放在 HBase,scan 顺序就是按 flow 维度查询的结果。

YARN Web UI 上的 “Timeline service” 标签页(默认 http://resourcemanager:8088/timeline)展示作业历史。如果配置正确,每个作业点进去可以看到 task 详情、counter、日志链接等。

模式提炼

Timeline Service v2 体现的设计模式:

1
2
3
4
5
6
7
模式:分布式写入 + 无状态读取 + LSM-Tree 后端

- 写入侧在每个数据生产者内嵌 Collector,本地攒批批量写
- 读侧无状态代理,通过 REST 转发到后端
- 后端用 LSM-Tree(HBase)按复合 rowkey 高效查询
- 数据模型支持多级聚合(Flow → Run → App → Entity)
- 与计算框架解耦——通过 Client 接口集成,框架主动上报

这个模式不只是 Timeline。Kafka 的 producer/consumer 也用类似的"本地攒批 + 批量网络"模式。Spark Event Log 同样是"AM 内部记录 + 持久化到 HDFS"的写入模式(差异在 Spark 用顺序追加文件,Timeline 用 HBase LSM-Tree)。Prometheus 的 Pushgateway 在某些场景类似——分布式客户端推指标到 Pushgateway,Prometheus 拉取。

数据模型的多级聚合(Flow → Run → App → Entity)对应"业务流水线 → 一次执行 → 一次任务 → 内部对象"。这种层级在很多监控系统中出现——OpenTelemetry 的 Trace / Span / Event 三级、ELK 的 Index / Document / Field、Prometheus 的 Metric / Label / Sample 都是类似抽象。

工程迁移表

Timeline v2 概念 Spark Event Log Flink JobManager Kafka Connect Prometheus
Per-App Collector Driver 内 Event Log writer JobManager 内 metric registry Worker 内 Convert 记录 pushgateway
Reader REST Spark History Server Flink REST API Connect REST PromQL API
HBase 后端 HDFS 顺序文件 内存 + RocksDB Kafka topic TSDB(自定义)
Flow 聚合 - - - Federation
多级聚合 - Job → Task → SubTask Connector → Task Metric → Label

注意 Spark Event Log 这一列。Spark 默认把事件写到 HDFS 上的一个顺序文件(.inprogress 后缀,作业完成后改名)。这种"一个文件追加写"的模式比 Timeline v2 简单,但只能 Spark 自己读。Timeline v2 把数据放到 HBase 让多框架(MR / Tez / Spark / Flink)共享存储和查询接口。

常见误解

误解一:“Timeline v2 只是 v1 的优化版本”。v2 与 v1 是完全不同的架构——v1 是集中式 Timeline Server,v2 是分布式 Per-App Collector + 无状态 Reader。架构差异巨大,迁移不是简单升级。Hadoop 3.0 早期 v2 还有 bug,到 3.1-3.2 才稳定,生产部署要谨慎。

误解二:“Timeline 数据必须实时查询”。Timeline 的写入是异步攒批,事件从产生到可查询有秒级延迟。如果需要实时监控(毫秒级),Timeline 不适合,应该用 Prometheus / OpenTelemetry 等专门的实时指标系统。

误解三:“Timeline v2 取代了日志聚合”。Timeline 记录的是结构化事件(task 启动、counter),日志聚合(YARN Log Aggregation)记录的是 stdout/stderr 文本日志。两个系统职责不同,互不替代。作业调试既要看 Timeline 的事件,也要看聚合日志的 stderr。

误解四:“HBase 后端是 Timeline v2 的可选配置”。Timeline v2 强制使用 HBase,不能换成其他后端。如果集群没有 HBase,Timeline v2 无法启用。这是为什么小集群经常不开 Timeline v2——HBase 部署成本太高,不如直接用日志聚合。

误解五:“Timeline 数据应该长期保留”。Timeline 数据是作业历史,理论上对调试和审计有价值。但 HBase 存储成本高,长期保留所有事件会让 HBase 集群膨胀。生产环境通常设置 TTL(默认 7 天,关键事件更长),让旧数据自动过期。

练习

  1. 在启用 Timeline v2 的集群上提交一个 MapReduce 作业,用 curl http://timeline-reader:8198/ws/v2/timeline/apps/{appId}/entities/YARN_APPLICATION 查询作业事件。观察 entity 的 attributes 和 timestamps。

  2. 在 HBase shell 里 scan 'timelineservice.app_flow_table', {LIMIT => 5},观察 Timeline 数据的 rowkey 结构(cluster + flow + run + app 复合)。

  3. apache/hadoop 源码里找到 TimelineCollector.javaTimelineReaderServer.javaHBaseTimelineWriterImpl.javaHBaseTimelineReaderImpl.java(hadoop-yarn-server-common-timeline / hadoop-yarn-server-timeline-plugin 模块),观察 v2 架构的核心实现。

  4. 思考题:如果让 Timeline v2 后端换成 HBase 之外的存储(例如 ClickHouse 或 TimescaleDB),需要改写哪些组件?这种替换在什么场景有价值?

系列导航

序号 主题 状态
00-06 HDFS 存储层(00 导读 / 01-06 HDFS 各主题) 第一阶段(已完成)
07 YARN 架构:ResourceManager、NodeManager、ApplicationMaster 的三方契约
08 资源模型:Resource、Container 与 NodeLabel
09 调度器对比:FIFO、Capacity、Fair 的设计取舍
10 应用程序生命周期:提交、调度、启动、运行、完成
11 YARN HA 与 Federation 上一篇
12 YARN Timeline Service v2:通用的应用历史与指标 本篇(YARN 篇完结)
13-15 MapReduce 计算模型(编程模型 / Shuffle / MRv2 on YARN) 第三阶段,下一篇开始

参考资料