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

Timeline Service 常被介绍成"YARN 的作业历史记录"。这个描述对应了功能但漏了 v2 与 v1 的架构差异。准确的说法是:Timeline Service v2 是 Hadoop 3.0 引入的下一代架构。与 v1 的"集中式 Timeline Server"不同,v2 把写入侧改造成 AM 侧应用级 collector 加 NodeManager 内嵌 collector;读侧改造成独立 TimelineReader + 可扩展 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
写入侧改造:
- 每个应用有自己的 application-level TimelineCollector(通常与 AM 同节点)
- 每个 NM 内嵌 NM TimelineCollector(container 事件聚合)
- Collector 在本地内存攒批,批量写到后端
- 写入负载分散到所有 AM / NM,不再集中在单 Timeline Server

读取侧改造:
- TimelineReader 是独立读服务;3.4.1 文档仍把 reader 描述为单实例
- Reader 只接受 REST 请求,转发到 HBase
- 读路径与写入路径解耦,不再让同一个 Timeline Server 同时承担写入和查询

后端改造:
- 3.4.1 官方路径默认以 HBase 作为 writer / reader 后端
- schema 按"flow run + app + entity"三级聚合,支持跨 app 查询
- HBase 的 LSM-Tree + RegionServer 让写入吞吐高、按 rowkey 查询快

这种"分布式写入 + 独立读取 + LSM-Tree 后端"的组合让 Timeline v2 比 v1 更容易扩展。事件被分散到 application-level collector 和 NM collector,再批量写 HBase;读取侧通过 TimelineReader 的 REST API 查 HBase。

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

Per-App TimelineCollector

v2 的核心创新是 application-level TimelineCollector。Hadoop 3.4.1 文档把它描述为与对应 AM 共置、在本版本中作为 NodeManager auxiliary service 运行的 collector。AM 内部产生的事件(task 启动、task 完成、counter 更新)先交给该应用的 collector,再由 collector 写入后端。

1
2
3
4
5
6
7
8
AM 所在节点:
├─ ApplicationMaster 主逻辑
└─ Application-level TimelineCollector(应用级事件入口)

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

Collector 会按配置周期 flush 到 HBase,yarn.timeline-service.writer.flush-interval-seconds 在 Hadoop 3.4.1 的默认值是 60 秒。这里不要写成固定的环形 buffer,也不要写成"减少 100 倍"这类未验证性能数字;可确认的机制是分布式 collector 承担写入入口,后端由 HBase 存储。

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 的读取侧组件,默认 HTTP 地址是 ${yarn.timeline-service.hostname}:8188,HTTPS 地址是 ${yarn.timeline-service.hostname}:8190。3.4.1 的 REST 根路径固定在 /ws/v2/timeline/

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

这套 REST API 支持 flow / run / app / entity 四个层级的查询。省略 cluster 的 endpoint 会使用 yarn.resourcemanager.cluster-id,而 flow/run 相关 endpoint 在 3.4.1 文档里包含 users/{user name} 这一层路径。

Reader 是独立的查询服务,它把 REST 查询转成对 HBase 后端的读取。3.4.1 文档写明 reader 是单实例;如果要做多实例负载均衡,需要按具体部署能力单独验证,不能把它写成默认架构。

Hadoop 自带的 YARN UI 可以通过 TimelineReader REST API 查询应用、attempt、container 和框架 entity。第三方 UI 是否读取 Timeline v2 数据取决于各自集成,不能从 TimelineReader 的存在反推出已经支持。

与计算框架的集成

Timeline v2 的价值依赖计算框架主动上报事件。Hadoop 3.4.1 文档明确说 Distributed Shell 和 MapReduce 可以写入 per-framework 数据,其他框架要以各自官方集成为准:

Distributed Shell:作为 YARN 示例应用,可以写入 Timeline Service v2。

MapReduce(MRv2):启用 mapreduce.job.emit-timeline-data=true 后,MapReduce 框架数据可以通过 MRAppMaster 写入 Timeline Service v2。

其他 AM 框架需要使用 Timeline v2 client 主动上报 entity、event 和 metric。AM 使用 AMRMClient 时可以注册 Timeline v2 client;否则需要从 AllocateResponse 里的 collector info 取得 collector 地址。框架不集成时,Timeline v2 仍可保留 YARN 通用生命周期事件和系统指标,但框架内部的 task、stage、counter 细节不会自动出现。

实验:观察 Timeline Service

实验状态:UNVERIFIED_RUNTIME。下面是按 Hadoop 3.4.1 文档整理的验证步骤和查询路径,本轮没有连接真实 YARN + HBase 集群执行。

部署 Timeline v2 的集群,配置 yarn.timeline-service.enabled=trueyarn.timeline-service.version=2.0f。如果同时启用 1.5 和 v2,则配置 yarn.timeline-service.versions=1.5f,2.0f;未配置 versions 时,它回落到 yarn.timeline-service.version。TimelineReader 默认 HTTP 端口是 8188。提交一个启用 mapreduce.job.emit-timeline-data=true 的 MapReduce 作业后,用 REST API 查询作业事件:

1
2
3
4
curl http://timeline-reader-host:8188/ws/v2/timeline/apps/application_1721900000_0001/entities/YARN_APPLICATION
curl http://timeline-reader-host:8188/ws/v2/timeline/clusters/mycluster/users/alice/flows/
curl http://timeline-reader-host:8188/ws/v2/timeline/clusters/mycluster/users/alice/flows/DailyETL/runs/1721952000000
curl http://timeline-reader-host:8188/ws/v2/timeline/clusters/mycluster/users/alice/flows/DailyETL/runs/1721952000000/apps

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

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

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

可以看到 Timeline 数据按 rowkey 存放在 HBase。表名前缀默认是 prod.,具体表名来自 Hadoop 3.4.1 的 Timeline Service v2 schema,而不是 timelineservice.* 这类通配命名。

Reader 根路径 http://timeline-reader-host:8188/ws/v2/timeline/ 会返回服务与版本信息。RM Web UI 是否跳到 Timeline 页面取决于部署的 UI 版本和集成方式,排障时先验证 Reader REST,再看 UI。

模式提炼

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 也用类似的本地攒批与批量网络写入。Spark Event Log 是 driver 记录事件并持久化到文件系统的模式,差异在 Spark 用顺序追加文件,Timeline 用 HBase LSM-Tree。Prometheus Pushgateway 适合短生命周期 batch job 暂存指标,不能替代通用分布式时序库。

数据模型的多级聚合(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
Application-level Collector Driver 内 Event Log writer JobManager 内 metric registry Worker 内状态上报 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 默认把事件写到文件系统上的顺序日志,History Server 再读取这些日志。Timeline v2 把数据放到 HBase,提供统一的 YARN entity 查询接口;具体计算框架是否把自己的内部事件写进去,要看框架自身集成。

常见误解

误解一:“Timeline v2 只是 v1 的优化版本”。v2 与 v1 是完全不同的架构——v1 是集中式 Timeline Server,v2 是分布式 application-level collector + 独立 TimelineReader。架构差异巨大,迁移不是简单升级。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 的可选配置”。Hadoop 3.4.1 的 Timeline v2 文档把 HBase 写成主后端,默认 writer/reader 都是 HBase storage 实现;同页部署步骤也要求准备 HBase、启用 coprocessor、创建 schema。如果集群没有 HBase,按官方路径就无法完整启用 v2。

误解五:“Timeline 数据应该长期保留”。Timeline 数据是作业历史,理论上对调试和审计有价值。但 HBase 存储成本高,长期保留所有事件会让 HBase 集群膨胀。v1 的 Timeline Server 文档有 7 天 TTL 默认值;v2 的保留策略要按 HBase schema、表 TTL 和集群策略配置,不能直接把 v1 TTL 当成 v2 行为。

练习

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

  2. 在 HBase shell 里列出 prod.* 表并抽样扫描应用到 flow 的映射表,观察 Timeline 数据的 rowkey 结构。

  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 导读:节点总会失败
01 HDFS 架构与三层切分
02 文件写入路径与流水线
03 文件读取路径与副本选择
04 NameNode 内存模型与启动恢复
05 HDFS HA 与脑裂防御
06 HDFS 3.x 演进与纠删码
07 YARN 架构与三方契约
08 YARN 资源模型、Container 与 NodeLabel
09 YARN 调度器对比:FIFO、Capacity、Fair
10 YARN 应用程序生命周期
11 YARN HA 与 Federation 上一篇
12 YARN Timeline Service v2 本篇
13 MapReduce 编程模型与分而治之 下一篇
14 MapReduce Shuffle 全流程
15 MRv2 on YARN ApplicationMaster 与 Task Attempt
16 Hadoop RPC 协议栈
17 序列化与压缩
18 Hadoop 安全 Kerberos Token ProxyUser
19 监控与运维 Metrics JMX 日志聚合
20 Hadoop 生态 Hive HBase Pig
21 Hadoop 与对象存储 Kubernetes 演进对比
22 Hadoop 设计遗产从 GFS MapReduce 到云原生

参考资料