Stream分组,即消息的分区(partition)方法。Storm中提供若干种实用的分组方式,包括Shuffle、Fields、All、Global、None、Direct和Local or shuffle等。
除了在Storm中内置的7中分组方式,也可以通过实现CustomStreamGrouping接口来定义自己的分组。
(1)Shuffle分组:Task中的数据随机分配,可以保证同一级Bolt上的每个Task处理的Tuple数量一致。
(2)Fields分组:根据Tuple中的某一个Field或者多个Field的值来划分。比如Stream根据user-id的值来分组,具有相同user-id值的Tuple会被分发到相同的Task中。
(3)All分组:所有的Tuple都会分发到所有的Task上。
(4)Global分组:整个Stream会选择一个Task作为分发的目的地,通常是具有最新ID的Task。
(5)None分组:不关心如何在Task中做Stream的分发,目前等同于Shuffle分组。
(6)Direct分组:产生的数据的Spout/Bolt自己明确决定这个Tuple被Bolt的哪些Task所消费。如果使用Direct分组,需要使用OutputCollector的emitDirect方法来实现。
(7)Local or shuffle分组:如果目标Bolt中的一个或者多个Task和当前产生数据的Task在同一个Worker进程中,那么就走内部的线程通信间通信,将Tuple直接发给在当前Worker进程中的目的Task。否则,同Shuffle分组。
流分组让topology知道在组件之间如何发送tuple,记住spouts和bolts是被当作很多tasks并行在整个集群中的。
当一个运行bolt A的task发射了一个tuple到bolt B,那么他应该发射到那个task呢?
流分组回答了这个问题,他告诉Storm如何在set of task(任务组)之间发送tuple,在我们深入不同种类的流分组以前,我么看看storm-start里的另一个topology,wordCountTopology从一个spout中读取句子并且从WordCountBolt中获取某个单词出现的次数:
TopologyBuilder builder = new TopologyBuilder();
builder.setSpout("sentence", new RandomSentenceSpout(), 5);
builder.setBolt("split", new SplitSentence(), 8)
.shuffleGrouping("sentences");
builder.setBolt("count", new WordCount(), 12)
.fieldsGrouping("split", new Fields("word"));
SplitSentence把它接收到的每一个句子中的每一个单词当做tuple发射出去,WordCount在内存中维护了一个单词和数量的映射关系,每次WordCount接收到一个单词,他就更新单词的数量,然后发射新的单词数量。
当然还有一些不同种类的流分组。
基本的分组类型叫做 “乱序分组(shuffle grouping)” ,它将使 tuple 被随机发个一个 task,WordCountTopology
中 使用了乱序分组来从 RandomSentenceSpout
向 SplitSentence
发送 tuple, 这样所有的处理任务就能够被平均的分配到所有运行 SplitSentence
Bolt的 task 上。
一个更有趣的分组类型是 字段分组(fields grouping)
,SplitSentence
和 WordCount
之间使用了一个字段分组,WordCount
能够运作的一个极为重要的要求是相同的单词必须被发到同一个 task中,否则会有一个以上的 task 会接收到相同的单词,然后他们会发射错误的计数。字段分组使我们可以用字段将一个流分组,这使得相同字段的内容总是被分到同一个task中。由于 WordCount
在 word
字段上使用字段分组订阅了 SplitSentence
‘s 的输出流,这样相同的单词总是会进入到相同的task.
字段分组是流连接和流聚合以及许多其他用力的基本实现,究其原理,字段分组是通过 mod hashing(哈希的一种) 来实现的。