Storm可以保证每个Spout发射的消息会被所有的Bolt完全处理,这也就意味着程序员可以决定Bolt是否需要保证消息处理。
一、使用Anchoring机制实现可靠的Bolt
拓扑是一个消息(Tuple)沿着一个或多个分支的树节点,每个节点将ack(tuple)或者fail(tuple),这样当消息失败时Storm就会知道,并通知Spout重发消息。因为一个Storm拓扑运行在一个高度并发的环境中,跟踪原始Spout示例的最好办法是在消息Tuple中包含一个原始Spout的引用,这种行为(技术)被称为Anchoring(锚定)。
public class SplitSentence implements IRichBolt {
private OutputCollector collector;
public void prepare(Map conf, TopologyContext context, OutputCollector collector) {
this.collector = collector;
}
public void execute(Tuple tuple) {
String sentence = tuple.getString(0);
for(String word : sentence.split(" ")) {
collector.emit(tuple, new Values(word));
}
collector.ack(tuple);
}
public void cleanup() {}
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("word"));
}
}
锚点发生的语句在collector.emit(tuple, new Values(word))中,传递元组(emit方法)使Storm能够跟踪原始Spout。collector.ack(tuple)和collector.fail(tuple)告诉Spout知道每个消息的处理结果。当消息树上的每个消息已经被处理,Storm认为来自Spout的元组被完全处理。当消息树在一个可配置的超时内处理失败,一个元组被认为是失败的。
处理的每一个元组必须是确认或者失败,Storm会使用内存来追踪每个元组,如果不对每个元组进行确认/失败,最终会耗尽内存。
二、使用IBasicBolt接口实现自动确认
为了简化编码,Storm为Bolt提供了一个IBasicBolt接口,它会在调用execute()方法之后正确调用ack()方法,BaseBasicBolt类是该接口的一个实现,用来实现自动确认。例如在下面的代码中,SplitSentence类的execute(0方法中,元组被发射到BasicOutputCollector后自动锚定到输入元组
public class SplitSentence extends BaseBasicBolt {
public void execute(Tuple tuple, BasicOutputCollector collector) {
String sentence = tuple.getString(0);
for(String word : sentence.split(" ")) {
collector.emit(new Values(word));
}
}
public void declareOutputFields(OutputFieldDeclarer declarer) {
declarer.declare(new Field("word");
}
}