关于Storm Stream grouping
在Storm中, 开发者可以为上游spout/bolt发射出的tuples指定下游bolt的哪个/哪些task(s)来处理该tuples。这种指定在storm中叫做对stream的分组,即stream grouping,分组方式主要有以下7种
- Shuffle Grouping 或 None Grouping
- Fields Grouping
- All Grouping
- Global Grouping
- LocalOrShuffle Grouping
- Direct Grouping
1. Shuffle Grouping或None Grouping
1.1 定义
Shuffle grouping: Tuples are randomly distributed across the bolt's tasks in a way such that each bolt is guaranteed to get an equal number of tuples.
None grouping: This grouping specifies that you don't care how the stream is grouped. Currently, none groupings are equivalent to shuffle groupings. Eventually though, Storm will push down bolts with none groupings to execute in the same thread as the bolt or spout they subscribe from (when possible).
——官方文档
1.2 图示与解读
上游spout/bolt发射的tuples被随机地在下游bolt的tasks中选择一个来处理。如下图:
1.3 优缺点
优点:为tuple选择task的代价小;
bolt的tasks之间的负载比较均衡;
缺点:上下游components之间的逻辑组织关系不明显;
1.4 适用场景举例
Shuffle grouping的方式在实际Topology中十分常见。Storm-starter中的ExclamationTopology即为典型一例。
ExclamationTopology实现的功能是:word-spout不断地向exclaim1-bolt发射随机单词,exclaim1-bolt每收到一个单词就追加“!!!”然后再发射给exclaim2-bolt,exclaim2-bolt每收到一个单词也追加“!!!”然后发射出来,最终每个单词都被追加了“!!!!!!”。
下面是ExclamationTopology中的关键代码。主函数:
在主函数中首先通过TopologyBuilder的对象定义了id为word的spout与id为exclaim1和exclaim2的两个bolt,分别在TestWordSpout、ExclamationBolt中实现。setSpout/setBolt()的最后一个参数是该component的并行度,即task数目,例如有10个task并行运行word-spout。shuffleGrouping("word")定义exclaim1-bolt以shuffleGrouping的方式从word-spout接收tuple,即word-spout发射出的任何一个tuple被exclaim2-bolt中随机的一个task接收并处理。
下面是ExclamationTopology的运行结果:
在运行中,exclaim1-bolt有3个task,分别是:exclaim1:2/3/4。从上面的运行结果看,exclaim1-bolt在接收word-spout的tuple时采用随机选择task的方式:exclaim1:3/4/4/3/2/4/4/2,这是在主函数中shuffleGrouping("word")时定义的。
2. Fields Grouping
2.1 定义
The stream is partitioned by the fields specified in the grouping. For example, if the stream is grouped by the "user-id" field, tuples with the same "user-id" will always go to the same task, but tuples with different "user-id"'s may go to different tasks.
——官方文档
2.2 图示与解读
下游bolt的每个task只接受并处理某些user-id的tuples,其他的tuples由这个bolt的其他task接受并处理。如下图:
2.3 优缺点
优点:上下游components之间的逻辑组织关系显著;
缺点:付出为tuple选择task的代价;
bolt的tasks之间的负载可能不均衡,根据field字段而定;
2.4 适用场景举例
Fields grouping的方式在实际Topology中十分常见,主要用来做tuple的分类合并等。Storm-starter中的WordCountTopology即为典型一例。
WordCountTopology实现的主要功能是:spout-spout不断向split-bolt发射随机句子,split-bolt将收到的句子分割成单词并发射给count-bolt,count-bolt对收到的每个单词计数,并发射该单词的计数值,最终实现对句子中所有单词计数。
下面是WordCountTopology中的关键代码。主函数:
在主函数中,定义了spout-spout、split-bolt和count-bolt三个component,分别由RandomSentenceSpout、SplitSentence和WordCount实现。shuffleGrouping("spout")定义split-bolt以shuffleGroupig的方式从spout-spout接收tuple。fieldsGrouping("split", new Fields("word"))定义count-bolt以fieldsGrouping的方式从split-bolt接收tuple,分类的标准是split-bolt发射出的tuple中的word字段值,例如所有word字段为“apple”的tuple都进入count-bolt的同一个task。split-bolt发射的tuple的字段在类SplitSentence中的declareOutputFields()中定义:
下面是WordCountTopology的运行结果:
在运行中,count-bolt有12个task。从运行结果看到,包含单词“the”的tuple全部进入count:3这个task,而包含单词“seven”的tuple全部进入count:2这个task。这就是在主函数中定义的fieldsGrouping("split", new Fields("word"))的作用。
3. All Grouping
3.1 定义
The stream is replicated across all the bolt's tasks. Use this grouping with care.
——官方文档
3.2 图示与解读
上游spout/bolt发射出的任何一个tuple都会复制发送给下游bolt的所有task,相当于广播方式。如下图:
3.3 优缺点
优点:上游事件可以通知下游bolt中所有task;
缺点:tuple消息冗余,对性能有损耗,请谨慎使用;
3.4 适用场景举例
由于对性能的损耗,All Grouping的方式在实际Topology中并不常用。但在某些场景中,例如希望上游spout/bolt发出的任一tuple下游bolt的所有task都能收到并做不同的处理,all grouping显得不可或缺。下面以All Grouping的测试示例myAllGroupingTestTopology来说明它的作用。
myAllGroupingTestTopology的主要功能是:myIntsSpout-spout不断产生自增tuple,例如第一次发射tuple[1,2,3],第二次发射tuple[4,5,6]等;randomIntGetterBolt-bolt收到tuple时从三个字段中随机选择一个发射给testBolt-bolt;testBolt-bolt将收到的tuple重新发射出来。
下面是myAllGroupingTestTopology中的关键代码。主函数:
主函数中定义了myIntsSpout-spout、randomIntGetterBolt-bolt、testBolt-bolt三个组件。allGrouping("myIntsSpout")定义randomIntGetterBolt-bolt以allGrouping的方式从myIntsSpout-spout接收tuple,即myIntsSpout-spout发出的每个tuple会被复制发送给randomIntGetterBolt-bolt的每个task。
下面是myAllGroupingTestTopology的运行结果:
在运行中,myIntsSpout-spout有2个task:myIntsSpout:4/5,randomIntGetterBolt-bolt有3个task:randomIntGetterBolt:6/7/8。从运行结果看,由myIntsSpout:4发出的tuple[475,476,477]会被复制成3份,分别发送给randomIntGetterBolt-bolt:6/7/8。这就是主函数中定义allGrouping("myIntsSpout")的作用。
4. Global Grouping
4.1 定义
The entire stream goes to a single one of the bolt's tasks. Specifically, it goes to the task with the lowest id.
——官方文档
4.2 图示与解读
上游spout/bolt发射的所有tuple全部进入下游bolt的同一个task,通常选择下游bolt中id最小的task。如下图:
4.3 优缺点
优点:所有上游消息全部汇总,便于合并、统计等;
缺点:bolt的tasks之间的负载可能不均衡,id最小的task负载过重;
4.4 适用场景举例
GlobalGrouping方式在实际的Topology中比较常见。我们将WordCountTopology稍微改动一下,变成统计所有单词出现次数的前3,即TopNTopology,即可看到GlobalGrouping的作用。
TopNTopology完成的主要功能是:spout-spout不断向split-bolt发射随机句子,split-bolt将收到的句子分割成单词并发射给count-bolt,count-bolt对收到的每个单词计数,并向TopN-bolt发射该单词及其计数值,TopN-bolt收将收到的所有tuple统计汇总,计算并发射所有单词的TopN。
下面是TopNTopology的关键代码。主函数:
在主函数中定义了多个spout/bolt,参见2.4,不再赘述。主函数中定义了TopN-bolt,由TopN实现(如下图),并行度为3。globalGrouping("count")定义TopN-bolt以globalGrouping的方式从count-bolt接收tuple,即所有count-bolt发射的tuple均由TopN中id最小的task接收并处理。TopN对收到的每个tuple做如下处理:将tuple中的<word,count>放入HashMap中,并排序后取前3,随后发射,具体实现如下图:
下面是TopNTopology的运行结果:
在运行中,TopN-bolt有3个task。从运行结果来看,TopN-bolt收到的所有tuple均由TopN:1来处理,TopN-bolt的其他task并未参与处理。这就是主函数中定义globalGrouping("count")的作用。
5. LocalOrShuffle Grouping
5.1 定义
If the target bolt has one or more tasks in the same worker process, tuples will be shuffled to just those in-process tasks. Otherwise, this acts like a normal shuffle grouping.
——官方文档
5.2 图示与解读
如果下游bolt的某些task与上游spout/bolt的某些task运行在同一个worker进程中,那么上游spout/bolt的这些task所发射的所有tuples均由下游bolt的同进程的tasks来处理;否则,这种分组方式等同于shuffle grouping。如下图:
5.3 优缺点
优点:相对于ShuffleGrouping,因优先选择同进程task间传输而降低tuple网络传输代价,但因寻找同进程的task而消耗CPU和内存资源,因此应视情况来确定选择ShuffleGrouping或LocalOrShuffleGrouping;
缺点:上下游components之间的逻辑组织关系不明显;
5.4 适用场景举例
LocalOrShuffleGrouping在实际应用中较不常见,多数被ShuffleGrouping代替。Storm-starter中的例程没有使用LocalOrShuffleGrouping的Topology。下面通过改写ExclamationTopology成为myExclamationTopology来看LocalOrShuffleGrouping的作用。
myExclamationTopology的主要功能是:word-spout不断地向exclaim1-bolt发射随机单词,exclaim1-bolt每收到一个单词就追加“!!!”然后再发射给exclaim2-bolt,exclaim2-bolt每收到一个单词也追加“!!!”然后发射给testBolt-bolt,testBolt-bolt将收到的tuple不做处理再次发射出来,最终每个单词都被追加了“!!!!!!”。
下面是myExclamationTopology中的关键代码。主函数:
主函数中word-spout、exclaim1-bolt和exclaim2-bolt的定义参见1.4,不再赘述。localOrShuffleGrouping("exclaim1")定义exclaim2-bolt以localOrShuffleGrouping的方式从exclaim1-bolt接收tuple。
下面是myExclamationTopology的运行结果:
在运行中,exclaim1-bolt只有1个task:exclaim1:4,在进程6703中;exclaim2-bolt有3个task:exclaim2:5/6/7,分别在进程6702/6701/6703中。所以,exclaim1:4和exclaim2:7都处于6703中。从运行结果看,exclaim1发射的所有tuple全部由exclaim2:7处理。这是主函数中定义localOrShuffleGrouping("exclaim1")的作用。
6. Direct Grouping
6.1 定义
This is a special kind of grouping. A stream grouped this way means that the producer of the tuple decides which task of the consumer will receive this tuple. Direct groupings can only be declared on streams that have been declared as direct streams. Tuples emitted to a direct stream must be emitted using one of the emitDirect methods. A bolt can get the task ids of its consumers by either using the provided TopologyContext or by keeping track of the output of the emit method in OutputCollector (which returns the task ids that the tuple was sent to).
——官方文档
6.2 图示与解读
Direct Grouping允许上游spout/bolt决定其发射出的任一tuple由下游bolt的哪个task接收并处理。如下图:
使用Direct Grouping需进行如下四步:
1)在上游spout/bolt的declareOutputFields()函数的实现中通过调用OutputFieldsDeclarer.declareStream(String streamId, boolean direct, Fields fields)函数来声明该上游spout/bolt的一个direct stream,其中streamId表示该stream的名字,fields表示在该stream上发送的tuple的字段,direct目前尚未发现有什么作用,将其设置为true/false都可以实现direct Grouping,但推荐设置为true,因为在未来的storm版本中direct参数可能有作用。
此外,经过试验,如果此处不通过OutputFieldsDeclarer.declareStream (String streamId, boolean direct, Fields fields)函数来声明一个direct stream,而是通过其他函数OutputFieldsDeclarer.declareStream(String streamId, Fields fields)或declare(Fields fields)来声明一个普通stream或default stream,这样的stream也可以被用来实现direct grouping。但依然推荐使用OutputFieldsDeclarer.declareStream(String streamId, boolean direct, Fields fields)函数来声明一个direct stream从而实现directGrouping,原因是在未来的storm版本中directGrouping可能只能通过direct stream支持direct grouping。
2)在Topology定义中通过调用setBolt(下游bolt).directGrouping (String componentId, String streamId)定义上游spout/bolt到下游bolt的directGrouping,其中setBolt()和directGrouping()都返回InputDeclarer对象,componentId表示上游spout/bolt的Id,streamId表示上游spout/bolt的一个direct stream的Id。
3)此时可以在上游spout/bolt中(如execute()函数的实现)调用emitDirect(int taskId, String streamId, Tuple anchor, List<Object> tuple)函数,但在此之前需要获得下游bolt的所有taskId以便调用。在spout/bolt的open()或prepare()函数实现中通过调用TopologyContext.getCompoentTasks(String componentId)可以得到下游bolt的所有taskId。
4)在上游spout/bolt中(如execute()函数的实现)通过调用OutputCollector.emitDirect(int taskId, String streamId, Tuple anchor, List<Object> tuple)函数利用该spout/bolt的direct stream向下游bolt的特定task发射tuple。其中taskId表示下游bolt指定的task的Id;streamId表示上游spout/bolt的一个direct stream的Id;tuple表示需要发射的tuple;anchor表示输入tuple,storm以此来建立tuple树,便于追踪(OutputCollector. ack(Tuple input)是用来做ack的。参考:Storm原理与实现-星星的日志-网易博客.pdf中1.2.1 普通Topology)。
6.3 优缺点
优点:Topology的可控性强,且组件的各task的负载可控;
缺点:当实际负载与预估不符时性能削弱;
6.4 试用场景举例
DirectGrouping在实际Topology中较不常见,在Storm-starter的例程中没有使用DirectGrouping的Topology。下面通过改写ExclamationTopology成为myStreamTestTopology来看DirectGrouping的作用。另外,在Oreilly.Getting.Started.with.Storm.Aug.2012.pdf中p24也有一个例子,通过directGrouping方式实现FieldGrouping。
myStreamTestTopology的主要功能是:word-spout不断地向exclaim1-bolt发射随机单词,exclaim1-bolt每收到一个单词分别追加“!”和“!!”然后以directGrouping的方式再发射给testBolt-bolt的第1个(起始为0)task,testBolt-bolt每收到一个tuple取其第一个字段发射给testBolt2-bolt,testBolt2-bolt将收到的tuple不做处理再次发射出来。
下面是myStreamTestTopology的关键代码。主函数:
主函数中word-spout,exclaim1-bolt,testBolt-bolt,testBolt2-bolt的定义参见1.4,不再赘述。directGrouping("exclaim1","outPutStream1")定义exclaim1-bolt以directGrouping的方式向testBolt-bolt发射tuple。
在exclaim1-bolt中首先定义名为outPutStream1/2的两个direct stream,然后得到testBolt-bolt的所有taskId,最后调用emitDirect()通过outPutStream1将新tuple发射到testBolt-bolt的第1个(起始为0)task。其具体实现如下图:
下面是myStreamTestTopology的运行结果:
在运行中,testBolt-bolt共有三个task:testBolt:6/7/8。由于exclaim1-bolt中指定tuple由testBolt-bolt的第1个task,即testBolt:7来接收并处理。从运行结果来看,exclaim1-bolt发射的tuple均由testBolt:7处理。这是directGrouping的作用。