storm源码之storm代码结构【译】

说明:本文翻译自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.coordinationbacktype.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"
 
 
 
标签: storm

storm源码之storm代码结构【译】的更多相关文章

  1. storm源码之storm代码结构【译】【转】

    [原]storm源码之storm代码结构[译]  说明:本文翻译自Storm在GitHub上的官方Wiki中提供的Storm代码结构描述一节Structure of the codebase,希望对正 ...

  2. 【原】storm源码之storm代码结构【译】

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

  3. axios 源码解析(中) 代码结构

    axios现在最新的版本的是v0.19.0,本节我们来分析一下它的实现源码,首先通过 gitHub地址获取到它的源代码,地址:https://github.com/axios/axios/tree/v ...

  4. Vue.js 源码分析(一) 代码结构

    关于Vue vue是一个兴起的前端js库,是一个精简的MVVM.MVVM模式是由经典的软件架构MVC衍生来的,当View(视图层)变化时,会自动更新到ViewModel(视图模型),反之亦然,View ...

  5. jQuery 源码分析(一) 代码结构

    jQuery是一个Javascript库,它支持链式操作方式,即对发生在同一个JQuery对象上的一组动作,可以直接接连写无需要重复获取对象.这一特点使得JQuery的代码无比优雅,而且有强大的选择器 ...

  6. 【原】storm源码之mac os x编译twitter storm源码

    twitter storm是由backtype公司创始人nathanmarz一手研发和开源的流计算(实时计算)框架,堪称实时计算领域的hadoop.nathanmarz也是在mac os x环境下开发 ...

  7. storm源码之一个class解决nimbus单点问题【转】

    本文导读: storm nimbus 单节点问题概述 storm与解决nimbus单点相关的概念 nimbus目前无法做到多节点的原因 解决nimbus单点问题的关键 业界对nimbus单点问题的努力 ...

  8. storm源码之巧用java反射反序列化clojure的defrecord获取属性值

    [原创]storm源码之巧用java反射反序列化clojure的defrecord获取属性值 [原创]storm源码之巧用java反射反序列化clojure的defrecord获取属性值 storm源 ...

  9. Storm源码分析--Nimbus-data

    nimbus-datastorm-core/backtype/storm/nimbus.clj (defn nimbus-data [conf inimbus] (let [forced-schedu ...

随机推荐

  1. URAL 1404. Easy to Hack! (模拟)

    space=1&num=1404">1404. Easy to Hack! Time limit: 1.0 second Memory limit: 64 MB When Vi ...

  2. XCL-Charts绘画面积图(AreaChart) 案件1

    样本区域地图,发现区域图的时候把做向上注视位置图更具优势的管理. 在改变. 区域图网格和轴是不一样的处理与其它图, 它是用来表示其影响范围的覆盖范围,车桥无段伸出.在这里下处理. 代码: /** * ...

  3. StyleCop中有一些官方自己写好的检测规则下面就是英文的解释

    在StyleCop中有一些官方自己写好的检测规则下面就是英文的解释 文档规则 1.SA1600:ElementsMustBeDocumented元素必须添加注释 2.SA1601: PartialEl ...

  4. HTML5实现图片文件异步上传

    原文:HTML5实现图片文件异步上传 利用HTML5的新特点做文件异步上传非常简单方便,本文主要展示JS部分,html结构.下面的代码并未使用第三发库,如果有参照,请注意一些未展现出来的代码片段.我这 ...

  5. MySQL编程(0) - Mysql中文乱码问题解决方案

    MySQL 5.6 for Windows 解压缩版配置安装: http://jingyan.baidu.com/article/f3ad7d0ffc061a09c3345bf0.html MySQL ...

  6. poj3070--Fibonacci(矩阵的高速幂)

    Fibonacci Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9650   Accepted: 6856 Descrip ...

  7. 微信公众平台消息接口开发-封装weixin.class.php

    原文:微信公众平台消息接口开发-封装weixin.class.php 一.封装weixin.class.php 由于微信公众平台的通信使用的是特定格式的XML数据,每次接受和回复都要去做一大堆的数据处 ...

  8. 砸金蛋:jQuery+PHP实现的砸金蛋中奖程序

    原文 砸金蛋:jQuery+PHP实现的砸金蛋中奖程序 砸金蛋被广泛应用于庆典活动.商家促销.电视娱乐等场合,它的趣味.悬念能迅速活跃现场气氛.同样,我们也可以将砸金蛋应用到WEB网站上,用于开展线上 ...

  9. Oracle SQL in 超过1000 的解决方案

    处理 Oracle SQL in 超过1000 的解决方案 处理oracle sql 语句in子句中(where id in (1, 2, ..., 1000, 1001)),如果子句中超过1000项 ...

  10. HDU 1256 图片8

    图片8 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submis ...