JStorm 源码解析:整体架构
Apache Storm 是一个基于 ZK 协调的分布式任务实时调度系统,属于流式(实时)计算引擎的一类。在目前的大数据和人工智能背景下流式计算是公司大部分业务的刚性需求,能够实现在百十毫秒内完成对用户行为的计算并执行具体的策略,例如依据用户的行为对其实施风控等。
当下市面上已有很多流式计算引擎产品,但是 Storm 的出现基本上统一了这一领域,不过近几年也出现了一些新的产品可以撼动 Storm 的地位,比如 Apache Flink、Apache Spark-Streaming 等。不可否认的是,现阶段还是有很多公司的业务运行在 Storm 集群上,这样一个毫秒级延迟的分布式实时计算引擎还是有很多地方值得我们一起去探寻其设计与实现原理。
JStorm 架构设计
JStorm 是在 Storm 的基础上基于 java 语言重写而来(Storm 采用 java 和 clojure 混合开发),并在原来的基础上进行了多项改进,主要包括:
- 简化模型设计 :将 Task 映射为一个线程,而不仅仅是一个逻辑执行单元。
- 多维度资源调度 :包括 CPU、内存、网络,以及存储等维度。
- 网络通信层改造 :采用更高性能的 netty + disruptor 替换原来的 zmq + blockingQueue。
- 采样重构 :滚动时间窗口、优化缓存性能、增量采样时间,以及减少无谓数据等。
- 异步化处理 :将 nextTuple 和 ack/fail 逻辑分离开,并在 Worker 中采用单独线程负责进出数据的反序列化和序列化工作。
- HA 机制 :解决 Storm Nimbus 节点的单点问题。
JStorm 的整体架构图如下:
其中 W 表示 Worker,T 表示 Task。
从图中我们可以看到 JStorm 在设计上将集群中的节点分为 Nimbus 和 Supervisor 两类。其中 Nimbus 节点相当于整个集群的调度者,基于 ZK 对整个集群进行调度;Supervisor 节点则是整个集群中实际运行 Topology 的节点。在一个 Supervisor 节点中一般会启动多个 Worker 进程,每个 Worker 进程又包含多个 Task 线程。我们提交的 Topology 任务一般会包含多个组件(spout 和 bolt),每个组件依据其并行度配置会分配到相应数量的 Task 任务,而每一个 Task 任务都运行在对应的 Task 线程上面。JStorm 是一个重度依赖于 ZK 的分布式调度系统,所有的工作组件(Nimbus、Supervisor、Worker,以及 Task)都会与 ZK 进行交互上报和更新自己的运行状态,同时获取其他工作组件的运行状态来指导自己接下去的运行。
Topology 任务提交和运行的基本过程
下面我们简单陈述一下一个 Topology 任务从提交到运行的基本执行过程。
当我们按照 JStorm 的开发规范实现好自己的 Topology 之后,我们需要将其打成 jar 包并执行相应的命令将其发布到集群,这期间我们主要是和 Nimbus 节点进行通信,Nimbus 会启动一个 thrift 服务,而提交任务的过程实际上就是一次 RPC 请求的过程。
Nimbus 节点会为本次任务提交请求创建对应的传输通道,然后等待用户上传 Topology 的 jar 文件到本地。上传完成之后,Nimbus 节点会依据用户的配置,以及集群的运行状态开始为当前 Topology 制定运行方案,包括需要分配多少 Task,这些 Task 需要多少 Worker 进行执行,对应的 Worker 需要落地到哪些 Supervisor 节点才能保证集群的均衡等。当方案制定完成之后,Nimbus 会将运行方案写入 ZK 对应的路径下面,并告知用户本次任务提交成功。
Supervisor 节点会定期检查 ZK 的任务分配路径以确定是否有新的任务需要执行,如果正好任务是被分配给当前 Supervisor 节点,则 Supervisor 会从 Nimbus 节点下载当前 Topology 对应的 jar 文件,并按照 Nimbus 制定的运行方案在本地启动相应的 Worker 去执行 Topology 任务。同时 Supervisor 会监控本地 Worker 的运行状态,如果存在运行异常的 Worker,则将其 kill 掉并通知 Nimbus 重新分配。
Nimbus 节点作为调度者在实际中以单节点的形式运行,早期的 Storm 在设计上没有引入 HA 机制,所以对于 Nimbus 节点而言存在单点的隐患。虽然 Nimbus 上的数据都是无状态的,但是当 Nimbus 节点宕机之后,还是会在一定程度上影响整个集群的正常运行。JStorm 在改造时引入了 HA 机制,在 JStorm 中可以同时启动多个 Nimbus 节点,这些节点在初始时都是 Follower 角色,它们会将自身的节点信息上报给 ZK,然后依据优先级竞选成为 Leader,期间需要 ZK 的介入来保证竞选结果的一致,当 Nimbus Leader 宕机之后,候选的 Follower 会马上顶替一个上来,以保证集群的正常运行。
后记
对于 JStorm 的架构我们先从整体上介绍这么多,在后续的篇章中将会逐一展开来进行深入的分析,包括:
- 编程接口。
- 拓扑的构建和提交过程。
- 拓扑任务的资源分配过程。
- 基础线程模型。
- Nimbus 的启动和运行机制。
- Supervisor 的启动和运行机制。
- Workers 的启动和运行机制。
- ACK 机制。
最后约定一下,后续的篇章中如果不作特殊说明,均用 Storm 指代 JStorm。