说明:本文翻译自Storm在GitHub上的官方Wiki中提供的Storm代码结构描述一节Structure of the codebase,希望对正在基于Storm进行源码级学习和研究的朋友有所帮助。

Storm的源码共分为三个不同的层次。

首先,Storm在设计之初就考虑到了兼容多语言开发。Nimbus是一个thrift服务,topologies被定义为Thrift结构体.

Thrift优势 : 使得Storm可以被任意开发语言使用。

其次,Storm的所有接口都是Java语言来定义的。因此,尽管Storm中的很多功能实现都是Clojure代码,但是使用这些功能都必须通过Java API。这意味着Storm的所有特性对于Java来讲都是可用的。

第三,Storm的很大一部分实现都是Clojure代码。从代码行来看,差不多是一半Java代码,一半Clojure代码。但是由于Clojure在表达能力上更为见长,因此,实际上绝大多数逻辑的实现都是Clojure来做的。

接下来的小节里将会逐个详细解释这三个层次。

storm.thrift

要理解Storm的代码结构,首先需要看的是storm.thrift文件。

Storm使用了从这里folk出来的Thrift版本来自动生成代码。这个Thrift版本实际上是将所有的Java packages都重命名为"org.apache.thrift7"之后的Thrift 7。除此之外,它与Thrfit 7是完全一样的。之所以单独出这样一个Thrift版本一是考虑到Thrift缺少向后兼容,而是为了避免包名冲突以满足一些用户在他们自己的topologies中用到其他版本的thrift。

一个topology中的任何一个spout或bolt都会被用户指定一个唯一标识,称为"component id"。当描述1个bolt接收其他哪些spout或bolt的输出时需要用到这个"component id"。StormTopology结构中保存了1个map来保存"component id"到"component"的映射关系,这个映射关系包含所有的component类型(即所有的spout、bolt)。

Thrift对Spout或bolt的定义是相同的,因此我们只需要看一下bolt的thrift定义。它包含了1个"ComponentObject"结构和1个"ComponentCommon"结构。

"ComponentObject"即是bolt的实现实体。它可以是以下三个类型之一:

  1. 1个序列化的java对象(这个对象实现IBolt接口)
  2. 1个"ShellComponent"对象,意味着bolt是由其他语言实现的。如果以这种方式来定义1个bolt,Storm将会实例化1个ShellBolt对象来负责处理基于JVM的worker进程与非JVM的component(即该bolt)实现体之间的通讯。
  3. 1个"JavaObject"结构,这个结构告诉Storm实例化这个bolt所需要得classname和构造函数参数。这一点在你想用非JVM语言来定义topology时比较有用。这样,在你使用非JVM语言来定义topology时就可以做到既使用基于JVM的spout或bolt,同时又不需要创建并序列化它们的Java对象。

"ComponentCommon"定义了这个component的其他所有属性。包括:

  1. 这个component发射什么stream以及stream的元数据(是否是direct stream,stream中field的声明)
  2. 这个component接收什么stream(被定义在1个component_id到stream_id的map里,在stream做分组时用到)
  3. 这个component的并行度
  4. 这个component的配置项configuration

注意,在spout的结构中同样有"ComponentCommon"字段,因此,spout也是可以被声明接收其他的stream输入。然而,Storm Java API并没有提供一种方式指定spout接收什么stream,同时如果你在这里指定1个spout的输入声明,在提交这个topology时将会出现报错信息。之所以这样设计,是因为spout的输入声明不是让用户自己来使用的,而是Storm内部使用的。Storm会在内部自动向topology添加stream和bolt来构造acking framework,其中的两个stream就是从acker bolt发出给topology中的所有spout节点的。只要1个tuple树被检测到完成了或失败了,acker就会通过这两个stream分别发出"ack"或"fail"消息。将用户提交的topology转换成运行时的topology的代码可参见这里。

Java接口

Storm的接口定义都是Java接口。主要的接口如下:

  1. IRichBolt
  2. IRichSpout
  3. TopologyBuilder

这样定义这些接口的主要意图在于:

  1. 以Java语言来定义接口
  2. 基于此接口,可以做到在不同的场合,提供出各自最适合的默认实现基类

这一策略的实际运用可以参考BaseRichSpout类

Spout和bolt就是按照以上接口描述的方式被序列化到topology的Thrift定义结构中。

值得一提的一个细节是,IBolt、ISpout与IRichBolt、IRichSpout这两对接口是有区别的。它们主要区别是在"Rich"版本里增加了"declareOutputFields"方法。这样设计的原因是所有的输出stream的输出field声明都必须是在Thrift结构里的(这样就可以做到使用任何编程语言来声明了),但是用户又希望能够在自己的class中来声明stream输出field信息。为解决这个问题,"TopologyBuilder"在构造Thrift结构时就是通过调用"declareOutputFields"方法来得到输出field的声明,然后将其转换纳入Thrift结构。这个转换操作可以从"TopologyBuilder"代码中的这一段里看到。

接口实现

通过将Storm所有的接口都由Java语言来定义确保了Storm的所有功能对于Java来讲都是可使用的。同时,Java接口的使用也使得Java用户在使用Storm时体验更好。

应该说,Storm主要是由Clojure语言实现的。尽管从代码行数上看一半是Java一半是Clojure,但其实里面绝大多数的逻辑实现都是Clojure。有两个值得一提的例外就是DRPC和支持事务的topology,它们二者都纯Java实现的。这样做的主要目的是来展示如何基于Storm,实现Storm之上更高层次的抽象。DRPC和支持事务的topology的实现分别位于backtype.storm.coordination和backtype.storm.transactional包里。

这里总结了一份主要的Java包和Clojure命名空间的内容列表:

Java包

  • backtype.storm.coordination:实现了DRPC和事务性topology里用到的基于Storm的批处理功能。这个包里最重要得类是CoordinatedBolt
  • backtype.storm.drpc:DRPC的更高层次抽象的具体实现
  • backtype.storm.generated:自动生成的Thrift代码(利用这里folk出来的Thrift版本生成的,主要是把org.apache.thrift包重命名成org.apache.thrift7来避免与其他Thrift版本的冲突)
  • backtype.storm.grouping:包含了用户实现自定义stream分组类时需要用到的接口
  • backtype.storm.hooks:定义了处理storm各种事件的钩子接口,例如当task发射tuple时、当tuple被ack时。关于钩子的手册详见这里
  • backtype.storm.serialization:storm序列化/反序列化tuple的实现。在Kryo之上构建。
  • backtype.storm.spout:spout及相关接口的定义(例如"SpoutOutputCollector")。也包括了"ShellSpout"来实现非JVM语言定义spout的协议。
  • backtype.storm.task:bolt及相关接口的定义(例如"OutputCollector")。也包括了"ShellBolt"来实现非JVM语言定义bolt的协议。最后,"TopologyContext"也是在这里定义的,用来在运行时供spout和bolt使用以获取topology的执行信息。
  • backtype.storm.testing:包括了storm单元测试中用到的各种测试bolt及工具。
  • backtype.storm.topology:在Thrift结构之上的Java层,用以提供一个纯Java API来使用Storm(用户不需要了解Thrift的细节)。"TopologyBuilder"及不同spout和bolt的基类们也在这里定义。稍高一层次的接口"IBasicBolt"也在这里定义,它会使得创建某些特定类型的bolt会更加简洁。
  • backtype.storm.transactional:包括了事务性topology的实现。
  • backtype.storm.tuple:包括Storm中tuple数据模型的实现。
  • backtype.storm.utils:包含了Storm源码中用到的数据结构及各种工具类。

Clojure 命名空间

  • backtype.storm.bootstrap:包括了1个有用的宏来引入源码中用到的所有类及命名空间。
  • backtype.storm.clojure:包括了利用Clojure为Storm定义的特定领域语言(DSL)。
  • backtype.storm.cluster:Storm守护进程中用到的Zookeeper逻辑都封装在这个文件中。这部分代码提供了API来将整个集群的运行状态映射到Zookeeper的"文件系统"上(例如哪里运行着怎样的task,每个task运行的是哪个spout/bolt)。
  • backtype.storm.command.*:这些命名空间包括了各种"storm xxx"开头的客户端命令行的命令实现。这些实现都很简短。
  • backtype.storm.config:Clojure中config的读取/解析实现。同时也包括了工具函数来告诉nimbus、supervisor等守护进程在各种情况下应该使用哪些本地目录。例如:"master-inbox"函数会返回本地目录告诉Nimbus应该将上传给它的jar包保存到哪里。
  • backtype.storm.daemon.acker:"acker" bolt的实现。这是Storm确保数据被完全处理的关键组成部分。
  • backtype.storm.daemon.common:Storm守护进程用到的公共函数,例如根据topology的名字获取其id,将1个用户定义的topology映射到真正运行的topology(真正运行的topology是在用户定义的topology基础上添加了ack stream及acker bolt,参见system-topology!函数),同时包括了各种心跳及Storm中其他数据结构的定义。
  • backtype.storm.daemon.drpc:包括了DRPC服务器的实现,用来与DRPC topology一起使用。
  • backtype.storm.daemon.nimbus:包括了Nimbus的实现。
  • backtype.storm.daemon.supervisor:包括了Supervisor的实现。
  • backtype.storm.daemon.task:包括了spout或bolt的task实例实现。包括了处理消息路由、序列化、为UI提供的统计集合及spout、bolt执行动作的实现。
  • backtype.storm.daemon.worker:包括了worker进程(1个worker包含很多的task)的实现。包括了消息传输和task启动的实现。
  • backtype.storm.event:包括了1个简单的异步函数的执行器。Nimbus和Supervisor很多场合都用到了异步函数执行器来避免资源竞争。
  • backtype.storm.log:定义了用来输出log信息给log4j的函数。
  • backtype.storm.messaging.*:定义了1个高一层次的接口来实现点对点的消息通讯。工作在本地模式时Storm会使用内存中的Java队列来模拟消息传递。工作在集群模式时,消息传递使用的是ZeroMQ。通用的接口在protocol.clj中定义。
  • backtype.storm.stats:实现了向Zookeeper中写入UI使用的统计信息时如何进行汇总。实现了不同粒度的聚合。
  • backtype.storm.testing:包括了测试Storm topology的工具。包括时间仿真,运行一组固定数量的tuple然后获得输出快照的"complete-topology","tracker topology"可以在集群"空闲"时做更细粒度的控制操作,以及其他工具。
  • backtype.storm.thrift:包括了自动生成的Thrift API的Clojure封装以使得使用Thrift结构更加便利。
  • backtype.storm.timer:实现了1个后台定时器来延迟执行函数或者定时轮询执行。Storm不能使用Java里的Timer类,因为为了单测Nimbus和Supervisor,必须要与时间仿真集成起来使用。
  • backtype.storm.ui.*:Storm UI的实现。完全独立于其他的代码,通过Nimbus的Thrift API来获取需要的数据。
  • backtype.storm.util:包括了Storm代码中用到的通用工具函数。
  • backtype.storm.zookeeper:包括了Clojure对Zookeeper API的封装,同时也提供了一些高一层次的操作例如:"mkdirs"、"delete-recursive"

转载于:https://www.cnblogs.com/catkins/p/5395092.html

storm源码之storm代码结构【译】相关推荐

  1. Wayland 源码解析之代码结构

    来源:http://blog.csdn.net/basilc/article/details/8074895 获取.编译 Wayland 及其依赖库可参考 Wayland 官方网站的 Build 指南 ...

  2. [pytest源码3]-pluggy代码结构与核心设计

    前言 现在我们开始分析,按照demo代码顺序先进行简单分析. 个人拙见,有错请各位指出. 如果的我的文章对您有帮助,不符动动您的金手指给个Star,予人玫瑰,手有余香,不胜感激. GitHub plu ...

  3. JStorm与Storm源码分析(七)--BasicBoltExecutor与装饰模式

    在Storm中IBasicBolt的主要作用是为用户提供一种更为简单的Bolt编写方式,更为简单体现在Storm框架本身帮你处理了所发出消息的Ack.Fail和Anchor操作,而这部分操作是由执行器 ...

  4. JStorm与Storm源码分析(一)--nimbus-data

    Nimbus里定义了一些共享数据结构,比如nimbus-data. nimbus-data结构里定义了很多公用的数据,请看下面代码: (defn nimbus-data [conf inimbus]( ...

  5. JStorm与Storm源码分析(三)--Scheduler,调度器

    Scheduler作为Storm的调度器,负责为Topology分配可用资源. Storm提供了IScheduler接口,用户可以通过实现该接口来自定义Scheduler. 其定义如下: public ...

  6. JStorm与Storm源码分析(二)--任务分配,assignment

    mk-assignments主要功能就是产生Executor与节点+端口的对应关系,将Executor分配到某个节点的某个端口上,以及进行相应的调度处理.代码注释如下: ;;参数nimbus为nimb ...

  7. Storm源码分析之四: Trident源码分析

    Storm源码分析之四: Trident源码分析 @(STORM)[storm] Storm源码分析之四 Trident源码分析 一概述 0小结 1简介 2关键类 1Spout的创建 2spout的消 ...

  8. JStorm与Storm源码分析(四)--均衡调度器,EvenScheduler

    EvenScheduler同DefaultScheduler一样,同样实现了IScheduler接口,  由下面代码可以看出: (ns backtype.storm.scheduler.EvenSch ...

  9. JStorm与Storm源码分析(二)--任务分配,assignmen

    mk-assignments主要功能就是产生Executor与节点+端口的对应关系,将Executor分配到某个节点的某个端口上,以及进行相应的调度处理.代码注释如下: 1 ;;参数nimbus为ni ...

最新文章

  1. 自建MySQL和阿里云RDS的区别 有必要使用云数据库吗?...
  2. extundelete数据恢复实战!!!
  3. 工控安全要避开传统IT安全思路的几个“暗坑”
  4. 【C/C++开发】C++实现简单的线程池
  5. python 3.6
  6. 关于VS附加调试Unity引起卡顿无响应问题
  7. 【译】An Intro to TrueBit: A Scalable, Decentralized Computational Court.
  8. LeetCode----9. 回文数
  9. Django Rest Framework 视图和路由
  10. 1.原生js封装的获取某一天是当年的第几周方法
  11. Linux多命令协作:管道及重定向
  12. Fedora 17 下 Samba 服务快速设置
  13. 这种扯淡的嵌入式项目,尽量不要碰
  14. Android 自定义ImageView加载图片
  15. html页面的css样式、meta最常用的最基本最常规的配置参数
  16. 开机cpu风扇声音大_电脑开机就显示黑屏怎么办?
  17. JGG | 中科院微生物研究所王军团队发现肠系膜淋巴系统可能为肠-肝轴第二通路...
  18. 华为鸿蒙朱丹丹,8分钟 京东用户喜提全球首台华为鸿蒙系统荣耀智慧屏
  19. 【NAACL 2021】RCI:在基于 Transformer 的表格问答中行和列语义捕获
  20. 如何在VMware虚拟机中查看Linux的IP地址

热门文章

  1. Redis整合Spring Data Redis 开发key和value乱码
  2. python wait之后怎么起起来_python wait方法_Python条件类| 带有示例的wait()方法
  3. python入门之控制结构-循环结构_(一)Python入门-4控制语句:05while循环结构-死循环处理...
  4. 开发日记-20190914 关键词 汇编语言王爽版 第七章第八章
  5. Macbook 无法找到Centos启动盘问题解决
  6. 思科安全:加密流量威胁检测、加密流量威胁和恶意软件检测、识别无线干扰或威胁、Talos 情报源可加强对已知和新型威胁的防御、分布式安全异常检测...
  7. peewee flask pwiz 中使用模型 过一段时间报错 peewee.InterfaceError: (0, '') 的解决方式
  8. VMware中安装CentOS Linux release 7.4.1708 (Core)
  9. LuoguP3183 [HAOI2016]食物链 记忆化搜索
  10. 关于table和td的边框设置