Storm 1.4 Storm中数据流分组的概念

数据流分组定义了一个数据流中的tuple如何分发给topology中不同bolt的task。举例说明,在单词计数topology中,SplitSentenceBolt类指派了4个task。数据流分组决定了指定的一个tuple会分发到哪个task上。
builder.setBolt(SPLIT_BOLT_ID, splitBolt, 2).setNumTasks(4).shuffleGrouping(SENTENCE_SPOUT_ID);

Storm定义了七种内置数据流分组的方式:

1.Shuffle grouping(随机分组):随机分发tuple给bolt的各个task,每个bolt实例接收到相同数量的tuple。

2.Fields grouping(按字段分组):根据指定字段的值进行分组。例如,一个数据流根据”word”字段进行分组,所有具有相同”word”字段值的tuple会路由到同一个bolt的task中。

3.All grouping(全复制分组):将所有的tuple复制后分发给所有bolt task。每个订阅数据流的task都会接收到tuple的拷贝。

4.Globle grouping(全局分组):将所有的tuples路由到唯一一个task上。Storm按照最小的task ID来选取接收数据的task。(当选用全局分组方式时,设置bolt的task并发度是没有意义的,因为所有tuple都转发到同一个task上了。使用全局分组的时候,需要注意,因为所有的tuple都转发到一个JVM实例上,可能会引起Storm集群中某个JVM或者服务器出现性能瓶颈或崩溃。)

5.None grouping(不分组):在功能上和随机分组相同,是为将来预留的。

6.Direct grouping(指向型分组):数据源调用emitDirect()方法来判断一个tuple应该由哪个Storm组件来接收。只能在声明了指向型的数据流上使用。

7.Local or shuffle grouping(本地会随机分组):和随机分组类似,但是,会将tuple分发给同一个worker内的bolt task(如果worker内有接收数据的bolt task)。其他情况下,采用随机分组的方式。取决于topology的并发度,本地或随机分组可以减少网络传输,从而提高topology性能。

除了预定义好的分组方式外,还可以通过实现CustomStreamGrouping(自定义分组)接口来实现自定义分组:
public interface CustomStreamGrouping
    extends Serializable{
    public abstract void prepare(WorkerTopologyContext workertopologycontext, GlobalStreamId globalstreamid, List targetTasks);
    public abstract List chooseTasks(int i, List list);
}

prepare()方法在运行时调用,用来初始化分组信息,分组的具体实现会使用这些信息,决定如何接收task分发tuple。WorkerTopologyContext对象提供了topology的上下文信息,GlobalStreamId提供了待分组数据流的属性。targetTasks是分组所有待选task的标识符列表。通常,会将targetTasks的引用存在变量里作为chooseTasks()的参数。

chooseTasks()方法返回一个tuple发送目标task的标识符列表。它的两个参数是发送tuple的组件的id和tuple的值。

为了说明数据流分组的重要性,我们在topology中引入一个bug。首先,修改SentenceSpout的nextTuple()方法,使每个句子只发送一次:

	
public void nextTuple() {
    if(index < sentences.length){
	this.collector.emit(new Values(sentences[index]));
		index++;
    }
    Utils.sleep(1);
}

程序的输出结果是这样的:
a:2
ate:2
beverages:2
cold:2
cow:2
dog:4
don't:4
fleas:4
has:2
have:2
homework:2
i:6
like:4
man:2
my:4
the:2
think:2

然后将CountBolt中的按字段分组改为随机分组:
//builder.setBolt(COUNT_BOLT_ID, countBolt).fieldsGrouping(SPLIT_BOLT_ID, new Fields("word"));
builder.setBolt(COUNT_BOLT_ID, countBolt, 4).shuffleGrouping(SPLIT_BOLT_ID);

程序的运行结果是这样的:
a:1
ate:1
beverages:1
cold:1
cow:1
dog:1
don't:1
fleas:1
has:2
have:1
homework:1
i:2
like:1
man:1
my:2
the:1
think:1

结果是错误的,因为CountBolt的参数是和状态相关的:它会对收到的每个单词进行计数。在这个例子中,在并发状况下,计算的准确度取决于是否按照tuple的内容进行适当的分组。我们引入的bug只会在CountBolt并发实例超过一个时出现。

通常需要避免将信息存在bolt中,因为bolt执行异常或重新指派时,数据会丢失。一种解决方法是定期对存储的信息快照并放在持久性存储中,比如数据库。这样,如果task被重新指派就可以恢复数据。

以上来自:

猜你喜欢

转载自margaret0071.iteye.com/blog/2360675