2021SC@SDUSC
bolt源码分析(三)
2021SC@SDUSC
本文主要介绍一下bolt接口方面
Storm中定义的Bolt接口主要有IBolt 、IRichBolt、IBasicBolt和IBatchBolt
几者关系如下:
IBolt.java
IBolt定义了Bolt的功能集合,其代码如下:
public interface IBolt extends Serializable {
void prepare(Map<String, Object> topoConf, TopologyContext context, OutputCollector collector);
void execute(Tuple input);
void cleanup();
}
Bolt是Storm中的基础运行单位,当其启动并有消息输入时,将调用execute方法来进行处理。 与ISpout类似,IBolt对象在提交时也会被序列化为字节数组,具体的执行节点通过反序列化的方法得到该对象,并调用prepare回调方法c 用户应将复杂对象的初始化放在prepare回调方法中实现,以保证每个具体对象都可以正确初始化。
对象被销毁时,将调用cleanup回调方法,但是Storm并不保证该方法一定被执行。
通常,在execute方法的实现中会对输人消息进行处理,这有可能产生新消息需要发送到下游节点,最后还要对输入的消息进行Ack操作。如果消息处理失败,则需对输入的消息进行Fail操作,这是保证Ack消息系统可以正常工作的基础。
IRichBolt.java
package org.apache.storm.topology;
import org.apache.storm.task.IBolt;
public interface IRichBolt extends IBolt, IComponent {
}
IRichBolt需要同时实现IComponent以及IBolt接口,其含义是一个具有Bolt功能的组件。在实际使用中,IRichBolt是实现Topology组件的主要接口。
IBasicBolt.java
public interface IBasicBolt extends IComponent {
void prepare(Map<String, Object> topoConf, TopologyContext context);
void execute(Tuple input, BasicOutputCollector collector);
void cleanup();
}
IBasicBolt接口的定义与IBolt基本一致,具体的实现要求也与IBolt相同,它与IBolt的区别在于以下两点:
1、它的输出收集器使用的是BasicOutputCollector,并且该参数被放在了execute方法中而不是prepare中。
2、它实现了IComponent接口,这表明它可以用来定义Topology组件。
此接口存在的原因:
IBasicBolt的主要作用是为用户提供一种更简单的Bolt
编写方式。基于IBasicBolt编写的好处是Storm框架本身帮你处理了所发出消息的Ack 、 Fail和Anchor操作,这是由执行器BasicBoltExecutor实现的。
BasicBoltExecutor实现了IRichBolt接口,同时还包含了一个IBasciBolt成员变量用于调用的转发。它是基于装饰模式实现的,其定义如下:
public class BasicBoltExecutor implements IRichBolt {
public static final Logger LOG = LoggerFactory.getLogger(BasicBoltExecutor.class);
private IBasicBolt bolt;
private transient BasicOutputCollector collector;
public BasicBoltExecutor(IBasicBolt bolt) {
this.bolt = bolt;
}
public void declareOutputFields(OutputFieldsDeclarer declarer) {
bolt.declareOutputFields(declarer);
}
@Override
public void prepare(Map<String, Object> topoConf, TopologyContext context, OutputCollector collector) {
bolt.prepare(topoConf, context);
this.collector = new BasicOutputCollector(collector);
}
public void execute(Tuple input) {
collector.setContext(input);
try {
bolt.execute(input, collector);
collector.getOutputter().ack(input);
} catch (FailedException e) {
if (e instanceof ReportedFailedException) {
collector.reportError(e);
}
collector.getOutputter().fail(input);
}
}
public void cleanup() {
bolt.cleanup();
}
public Map<String, Object> getComponentConfiguration() {
return bolt.getComponentConfiguration();
}
}
BasicBoltExecutor需要实现IRichBolt接口的原因:
用户实现了IBasicBolt接口的Bolt对象以后,在构建Topology时,Storm会调用TopologyBuilder的setBolt方法设置该Bolt对象。SetBolt方法会用BasicBoltExecutor封装用户的实现类,这是Storm自动帮用户实现的,而且它还会调用可接收IRichBolt参数的重载方法完成Bolt设置。
IBatchBolt.java
public interface IBatchBolt<T> extends Serializable, IComponent {
void prepare(Map<String, Object> conf, TopologyContext context, BatchOutputCollector collector, T id);
void execute(Tuple tuple);
void finishBatch();
}
区别于IBasicBolt接口,IBatchBolt主要用于Storm中的批处理。目前,Storm主要用该接口来实现可靠的消息传输,在这种情况下,批处理会比单一消息处理更为高效。Storm的事务Topology以及Trident主要是基于IBatchBolt的。
相比前面的IBolt、IBasicBolt和IRichBolt, IBatchBolt中多了一个finishBatch方法,它在一个批处理结束时被调用。
prepare方法:
用来初始化一个Batch。在prepare方法中,最后一个参数是通用类型T,它可以用作该Batch的唯一标识。在IBatchBolt衍生的BaseTransactionalBolt中, T将被实例化为TransactionalAttempt。
在目前的Storm实现中,每个事务都会对应一个Batch,而每个Batch的数据都会由一个新仓健的IBatchBolt对象进行处理。于是在prepare方法中,需要传人一个用于标识batch的变量T。而在事务Topology中,Storm则利用TransactionAttempt作为标识。当一个Batch被成功处理之后,该Batch对应的IBatchBolt对象将被销毁,因此用户不能通过IBatchBolt对象自身保存需要在多个Batch间进行共享的数据。
execute方法:
用于处理属于该Batch的消息。
finishBatch方法:
该方法仅当这批消息被处理完时才会被调用。如果BatchBolt同时实现
了ICoranitter的接口,finishBatch方法只有当该Batch之前的所有Batch均被成功处理后才被调用。这既保证了强序关系,同时也是Storm中事务Topology的实现基础。