Spout
为了管理Spout的可靠性,可以在发射元组的时候,在元组里面包含一个消息ID: collector.emit(new Values(..),tupleId);
当元组处理时会调用ack()方法,当元组失败时调用fail()方法,当元组被所有目标Bolt和锚定Bolt处理时,认为元组处理成功。
当如下情况发生时,元组处理会失败:
collectior.fail(tuple)方法补目标Spout调用 。
处理时间超过配置的超时时间。
Bolt
bolt的主要方法:
declareOutputFileds(OutputFieldsDeclarer declarer) ;
prepare(Map conf,TopologyContext context,OutputCollector collector); 仅在Bolt开始处理Tuple之前调用;
execute(Tuple input);处理一个bolt的输入
clearup();当bole关闭时调用
可靠的与不可靠的Bolt
可靠的Bolt
@Override
public void execute(Tuple input) {
String sentence = input.getString(0);
for (String word : sentence.split(" ")){
collector.emit(input,new Values(word));//发射时带上元组
}
//手动调用ack()方法,保证消息处理成功
collector.ack(input);
}
不可靠的Bolt
@Override
public void execute(Tuple input) {
String sentence = input.getString(0);
for (String word : sentence.split(" ")){
collector.emit(new Values(word));
}
}
使用Anchoring机制实现可靠的Bolt
拓扑是一个消息(Tuple)沿着一个或多个分支的树节点。每个节点将ack(tuple)或者fail(tuple),这样当消息失败时Storm就会知道,通知Spout重发消息。因为一个Storm拓扑运行在一个高度并行的环境中,跟踪原始Spout实例的最好方法是,在消息Tuple中包含一个原始Spout的引用,这种技术称为Anchoring。
下面是可以保证消息处理的SplitSentence类,实现IRichBolt接口:
public class SplitSentenceBolt implements IRichBolt{
private static final long serialVersionUID = 1L;
//定义发射器
private OutputCollector collector = null;
@Override
public void cleanup() {
}
@Override
public void execute(Tuple tuple) {
String sentence = tuple.getString(0);
for (String word : sentence.split(" ")){
//发射出去(带上元组以确保消息不丢失)
collector.emit(tuple, new Values(word));
}
//处理完成时调用ack方法
collector.ack(tuple);
}
@Override
public void prepare(Map conf, TopologyContext context, OutputCollector collector) {
//初始化
this.collector = collector;
}
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("word"));
}
@Override
public Map<String, Object> getComponentConfiguration() {
return null;
}
}
Anchoring发生的语句在collector.emit()中。如前所述,传递元组使Storm能够跟踪原始Spout。collector.ack(tuple)和collector.fail(tuple)告诉Spout知道每个消息发生了什么事,当消息树上的每个消息已经被处理,Storm认为来自Spout的元组被完全处理。当消息树在一个可配置的超时内处理失败,一个元组被认为是失败的。超时的默认值是30秒。可以通过改变拓扑的Config.TOPOLOGY_MESSAGE_TIMEOUT_SECS配置项的值来改变这个超时值。当然, Spout需要考虑消息失败后重试或者丢弃消息的情况。处理的每一个元组必须是确认或者失败。Storm使用内存来追踪每个元组,所以如果你不对每个元组进行确认/失败,最终会耗尽内存。
使用IBasicBolt接口实现自动确认
为了简化编码,Storm为Bolt提供了一个IBasicBolt接口,它会在调用execute()方法之后正确调用ack()方法。BaseBasicBolt类是该接口的一个实现,用来实现自动确认。例如,在下面代码中的execute()方法中,元组被发射到BasicOutputCollector后自动锚定到输入元组。
public class SplitSentenceBolt implements IBasicBolt{
private static final long serialVersionUID = 1L;
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("word"));
}
@Override
public Map<String, Object> getComponentConfiguration() {
return null;
}
@Override
public void cleanup() {
}
@Override
public void execute(Tuple tuple, BasicOutputCollector collector) {
try {
String sentence = tuple.getString(0);
for (String word : sentence.split(" ")){
//发射的时候直接发消息,不需要发送原来的tuple
collector.emit(new Values(word));
}
//不需要手动的去调用ack()方法
} catch (Exception e) {
//显示的抛出FailedException异常(Storm发现这个错误之后会自动处理)
throw new FailedException("消息处理失败");
}
}
@Override
public void prepare(Map conf, TopologyContext context) {
}
}
综上所述:
要实现可靠的Bolt,可以继承IBasicBolt或者实现IBasicBolt接口。
要实现不可靠Bolt,可以继承BaseRichBolt或者实现IRichBolt接口。
不可靠Bolt可以使用Anchoring机制实现可靠的Bolt
事务拓扑中的Bolt
在事务拓扑中存在3种类型的Bolt:
BasicBolt:BasicBolt不处理Batch,不能处理单个输入元组。
BatchBolt:BatchBolt能够处理元组Batch,Batch中的每个元组都会被调用 execute方法,当Batch处理完成时调用finishBatch方法。
storm会为每个batch创建这个一个BatchBolt对象的新实例。而这些BatchBolt是运行在BatchBoltExecutor里面的。
在事务拓扑中的所有元组必须以TransactionAttempt作为元组的第一个字段。这使storm可以识别元组属于哪个Batch.
标记为Committer的BatchBolt:该Bolt和普通BatchBolt的区别是调用 finishBatch的时机。一个CommitterBolt在提交阶段会调用 finishBatch.
CommitterBolt不会在事务处理阶段调用finishBatch方法
有两种方法可以使BatchBolt成为Committer BatchBolt,实现ICommitter接口或者使用TransactionalTopologyBuilder类的setCommiterBolt方法。
在transaction topology里面发射的所有的tuple都必须以TransactionAttempt作为第一个field,然后storm可以根据这个field来判断哪些tuple属于一个batch。所以你在发射tuple的时候需要满足这个条件。TransactionAttempt包含两个值: 一个transaction id,一个attempt id。transaction id的作用就是我们上面介绍的对于每个batch是唯一的,而且不管这个batch replay多少次都是一样的。 我们可以把attempt id理解成replay-times, storm利用这个id来区别一个batch发射的tuple的不同版本。transaction id对于每个batch加一, 所以第一个batch的transaction id是”1″, 第二个batch是”2″,以此类推。execute方法会为batch里面的每个tuple执行一次。最后, 当这个bolt接收到某个batch的所有的tuple之后, finishBatch方法会被调用。这个例子里面的BatchCount类会在这个时候发射它的局部数量到它的输出流里面去。