Strom 消息可靠性保障机制和Ack原理
Storm提供了消息处理的保障机制,可以保证从Spout发射出的每个tuple都得到完整的处理。当然Storm消息处理保障机制的前提是你使用了这种特性,如果你的业务对偶尔丢失的tuple不敏感,那么也没必要启用这种机制,毕竟有得就会有失。
注:本文前几节的内容主要来自并发编程网,原文链接:http://ifeve.com/storm-guaranteeing-message-processing/。最后一节“ Ack原理 ”是自己的理解,不足之处欢迎指正。
概述
“完整性处理”是什么意思?
一个从 spout 中发送出的 tuple 会产生上千个基于它创建的 tuples。例如,有这样一个 word-count 拓扑:
TopologyBuilder builder = new TopologyBuilder();
builder.setSpout("sentences", new KestrelSpout("kestrel.backtype.com",
22133,
"sentence_queue",
new StringScheme()));
builder.setBolt("split", new SplitSentence(), 10)
.shuffleGrouping("sentences");
builder.setBolt("count", new WordCount(), 20)
.fieldsGrouping("split", new Fields("word"));
这个拓扑从一个 Kestrel 队列中读取句子,然后将句子分解成若干个单词,然后将它每个单词和该单词的数量发送出去。这种情况下,从 spout 中发出的 tuple 就会产生很多基于它创建的新 tuple:包括句子中单词的 tuple 和 每个单词的个数的 tuple。这些消息构成了这样一棵树:
如果这棵 tuple 树发送完成,并且树中的每一条消息都得到了正确的处理,就表明发送 tuple 的 spout 已经得到了“完整性处理”。对应的,如果在指定的超时时间内 tuple 树中有消息没有完成处理就意味着这个 tuple 失败了。这个超时时间可以使用 Config.TOPOLOGY_MESSAGE_TIMEOUT_SECS 参数在构造拓扑时进行配置,如果不配置,则默认时间为 30 秒。
在消息得到完整性处理后或者处理失败后会发生什么?
为了理解这个问题,让我们先了解一下 tuple 的生命周期。下面是定义 spout 的接口:
public interface ISpout extends Serializable {
void open(Map conf, TopologyContext context, SpoutOutputCollector collector);
void close();
void nextTuple();
void ack(Object msgId);
void fail(Object msgId);
}
首先,通过调用 Spout 的 nextTuple 方法,Storm 向 Spout 请求一个 tuple。Spout 会使用 open 方法中提供的SpoutOutputCollector 向它的一个输出数据流中发送一个 tuple。在发送 tuple 的时候,Spout 会提供一个 “消息 id”,这个 id 会在后续过程中用于识别 tuple。例如,上面的 KestrelSpout 就是从一个 kestrel 队列中读取一条消息,然后再发送一条带有“消息 id”的消息,这个 id 是由 Kestrel 提供的。使用 SpoutOutputCollector 发送消息一般是这样的形式:
_collector.emit(new Values("field1", "field2", 3) , msgId);
随后,tuple 会被发送到对应的 bolt 中去,在这个过程中,Storm 会很小心地跟踪创建的消息树。如果 Storm 检测到某个 tuple 被完整处理, Storm 会根据 Spout 提供的“消息 id”调用最初发送 tuple 的 Spout 任务的 ack 方法。对应的,Storm 在检测到 tuple 超时之后就会调用 fail 方法。注意,对于一个特定的 tuple,响应(ack