storm 是一个老牌的实时流处理框架。Hadoop的Mapreduce, spark的RDD ,都受到函数式编程的启发,通过纯函数式的接口,不断地对数据进行变换,过滤,聚合,从而得到最终结果。

函数式的编程模型容易理解,容易编写,也容易测试。而storm的模型相比之下要更复杂和开放。

入门storm的第一步,就是厘清这些纠缠的,隐晦的编程模型。

这篇笔记参考了了storm官网的Concepts一章,加上自己的理解。

Topology

topology是storm的一个顶层概念,由许多Spout (消息生产者)和Bolt(消息生产者和消费者) 组成。就像Hadoop的Mapreduce Job , 或者Spark中的RDD DAG ,topology 也是一个有向无环图。 但与前两个模型不同,topology 处理的是实时数据,理论上数据流的长度是无限的,所以topology 不会因为计算完成而结束。

Mapreduce 计算的时候分为map 阶段和reduce 阶段,两者之间有先后关系。Spark的DAG 也被分成stage,stage按照先后顺序排队执行。对于Storm 来说,spout和 topology 没有明确的先后顺序,流数据的生产和消费过程一直存在。

Spout

spout 是流数据的生产者。也就是数据源。只生产不消费。所有的topology 都是从一个或者多个spout开始的。 enter image description here Spout 接口和类如上图所示。如果直接实现IRichSpout接口,就需要重写接口的所有方法,所以最常见的情况就是继承BaseRichSpout,然后重写open 和 nextTuple方法。

open 方法有三个参数,入门阶段只需要关心第三个参数,一个类型是SpoutOutputCollector的变量collector。通常Spout子类会定义一个SpoutOutputCollector 类的私有变量,然后在open方法中把collector赋值给 同类型的私有变量。

nextTuple就是Spout 发送数据的地方。Storm通过调用spout类的nextTuple方法来生产数据。这个方法必须是非阻塞的(non-blocking), 如果此时没有数据要发送,可以直接return 。

Bolt

bolt 是流数据的处理单元,它是数据的消费者,也可以是数据的生产者。你可以对数据进行变换,过滤,聚合,写入持久化的存储,等等等等。多个bolt 可以组成一个流处理流水线,也可能组成一个有向无环图,就像RDD的DAG那样。

Bolt 类型的核心是execute方法,这是Bolt类接收并处理消息的地方。Bolt 通过一个OutputCollector 对象发送处理过的消息。这个对象可能来自prepare方法,或者execute 方法。

Stream & Tuple

stream 顾名思义就是流处理的对象——数据流。数据流是一个无穷的序列,而序列中的每一个元素,就是tuple。storm的数据处理,也是以tuple为核心的。Bolt 类的execute的第一个参数就是Tuple对象。

    void execute(Tuple input);

如果你熟悉Python或者Scala,对于tuple就不会陌生。Storm中的tuple 实际上只是一个封装过的数据列表,类似Python中的named tuple 或者Scala 中的case class,可以通过index 访问字段,也可以通过字段名访问。 当你发送一个新的tuple时,通常会调用OutputCollector对象的emit 方法

public List<Integer> emit(List<Object> tuple) {
        return emit(Utils.DEFAULT_STREAM_ID, tuple);
    }

List<Object> 封装了一条消息。而每个字段的名字,通过调用declareOutputFields的方法来声明。 给字段起名字有什么用?这就跟Storm的Grouping机制有关系了。