OutputCollector

 在Storm中,Spout使用的是SpoutOutputCollector收集器,SpoutOutputCollector的接口就是ISpoutOutputCollector。ISpoutOutputCollector的源码如下:

public interface ISpoutOutputCollector extends IErrorReporter{
    /**
        Returns the task ids that received the tuples.
    */
    List<Integer> emit(String streamId, List<Object> tuple, Object messageId);
    void emitDirect(int taskId, String streamId, List<Object> tuple, Object messageId);
    long getPendingCount();
}

  emit方法用来向外发送数据,它的返回值是该消息所有发送目标的Taskld集合, 其输入参数streamld表示消息将被输出到的流;tuple为要输出的消息列表;messageld表示输出消息的标记信息,如果messageld被设置为null, Storm将不会追踪该消息,否则它会被用来追踪所发出消息的处理情况。 
  emitDirect方法的参数与emit方法相似,主要区别在于使用emitDirect时, 只有由参数taskld所指定的Task才可以接收这条消息。这个方法要求与参数streamld相对应的流必须被定义为直接流,同时接收端的Task也必须以直接分组 ( Direct Grouping ) 的方式来接收消息,否则会有异常抛出。另外,如果没有下游节点接收该消息,那么该消息其实也就没有被真正发送。

  SpoutOutputCollector实现了ISpoutOutputCollector,但它实际上是一个代理类,持有ISpoutOutputCollector类型的对象:

public class SpoutOutputCollector implements ISpoutOutputCollector {
    ISpoutOutputCollector _delegate;

  具体的执行都是通过_delegate调用相应的方法来实现的。

  Bolt输出收集器使用的是OutputCollector,不同类型的Bolt所使用的输出收集器也是不同的。 
  IRichBolt使用的是OutputCollector,该收集器实现的是IOutputCollector接口,实际上是一个代理类。 
  IBasicBolt使用BasicOutputCollector,该收集器实际上是OutputCollector的封装类,实现的是IBasicOutputCollector接口。 
  IBatchBolt使用BatchOutputCollector,是Storm中用于数据批处理的输出收集器,Storm提供 了 它 的 默 认 实 现 类 BatchOutputCollectorImpl, 这 个 类 实 际 上 也 是 通 过 封 装OutputCollector类来实现消息发送的。

  OutputCollector收集器的接口就是IOutputCollector。IOutputCollector的源码如下:

public interface IOutputCollector extends IErrorReporter {
    /**
     *  Returns the task ids that received the tuples.
     */
    List<Integer> emit(String streamId, Collection<Tuple> anchors, List<Object> tuple);
    void emitDirect(int taskId, String streamId, Collection<Tuple> anchors, List<Object> tuple);
    void ack(Tuple input);
    void fail(Tuple input);
    void resetTimeout(Tuple input);

  emit方法用来向外发送数据,它的返回值是该消息所有发送目标的Taskld集合,输入参数streamld表示消息将被输出到的流;anchors为输出消息的标记,通常代表该条消息是由哪些消息产生的,主要用于消息的Ack系统;tuple即要输出的消息,为一个Object列表。 
  emitDirect方法的输人列表与emit方法相似,主要区别在于, emitDirect发送的消息只有指定的Task才可以接收。 
  fail和ack方法用来表示消息是否被成功处理。

  IOutputCollector默认实现类OutputCollector,它实际上是一个代理类,持有IOutputCollector 类型的对象。

public class OutputCollector implements IOutputCollector {
    private IOutputCollector _delegate;

  emit、emitDirect等方法具体的执行都是通过_delegate调用相应的方法来实现的。

  BasicOutputCollector的接口是IBasicOutputCollector:

public interface IBasicOutputCollector extends IErrorReporter{
    List<Integer> emit(String streamId, List<Object> tuple);
    void emitDirect(int taskId, String streamId, List<Object> tuple);
    void resetTimeout(Tuple tuple);
}

  为什么会有IBasicOutputCollector? 这个接口是在IBasicBolt中使用的, 对比IOutputCollector可以看出它们的区别:IBasicOutputCollector没有Ack和Fail方法;IBasicOutputCollector的emit和emitDirect方法中没有anchor参数。 
  这样设计的原因是如果使用IBasicBolt, Storm框架会自动帮用户进行Ack、Fail和Anchor操作,用户自己不需要关心这一点。所以为了确保这种机制正常运行, 避免用户在使用时出错, Storm提供了简化版的IBasicOutputCollector。当然,简化也就意味着使用IBasicBolt是有限制的。 
  BasicOutputCollector是Storm提供的IBasicOutputCollector接口的默认实现,其中包含了一个OutputCollector类型的成员变量,实际上所有的消息最终都将由这个OutputCollector进行处理。BasicOutputCollector还提供了一些简易的方法进行消息标记。 
  IBasicBolt帮忙处理了发出消息的Ack、Fail和Anchor操作,而这部分操作是由执行器BasicBoltExecutor实现的。来看下BasicBoltExecutor怎么构造出来的。在TopologyBuilder#setBolt:

public BoltDeclarer setBolt(String id, IBasicBolt bolt) throws IllegalArgumentException {
    return setBolt(id, bolt, null);
}
public BoltDeclarer setBolt(String id, IBasicBolt bolt, Number parallelism_hint) throws IllegalArgumentException {
    return setBolt(id, new BasicBoltExecutor(bolt), parallelism_hint);
}

  这里就构造出来了一个BasicBoltExecutor,看一下BasicBoltExecutor的源码:

/**
 * BasicBoltExecutor实现了IRichBolt接口
 */
public class BasicBoltExecutor implements IRichBolt {
    public static Logger LOG = LoggerFactory.getLogger(BasicBoltExecutor.class);   
    //持有IBasicBolt类型的变量
    private IBasicBolt _bolt;
    //定义了成员变量_collector
    private transient BasicOutputCollector _collector;

    public BasicBoltExecutor(IBasicBolt bolt) {
        _bolt = bolt;
    }
    /**
     * 实现declareOutputFields方法,但它实际上是
     * _bolt调用declareOutputFields方法
     */
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        _bolt.declareOutputFields(declarer);
    }

    /**
     * 实现prepare方法,
     * 实际上是调用_bolt的prepare方法,
     * 并实例化BasicOutputCollector
     */
    public void prepare(Map stormConf, TopologyContext context,
            OutputCollector collector) {
        _bolt.prepare(stormConf, context);
        _collector = new BasicOutputCollector(collector);
    }
    /**
     * 实现execute方法
     */
    public void execute(Tuple input) {
        //设置运行器上下文,
        //它表示经execute方法发送出去的消息都是由输入消息产生的,
        //即输出的消息都将标记为输入消息所衍生出来的消息,
        //这是使用IBasicBolt实现消息跟踪的重要一环
        _collector.setContext(input);
        try {
            //调用_bolt的execute方法
            _bolt.execute(input, _collector);
            //对输入的消息进行Ack操作.
            //这一步意味着基于当前输入消息的处理和衍生消息的发送已经完成,
            //这时就可以对该消息进行Ack操作了.
            _collector.getOutputter().ack(input);
        } catch(FailedException e) {
            //Storm捕获所有的FailedException,并对输入的消息进行Fail操作。
            //如果捕获的异常为ReportedFailedException的实例,
            //则调用reportError回调方法,给用户一个机会去处理异常
            //FailedException是Storm定义的一种基本异常,用来进行消息的失败重发等操作,
            //并不会导致Topology运行停止
            if(e instanceof ReportedFailedException) {
                _collector.reportError(e);
            }
            _collector.getOutputter().fail(input);
        }
    }
    public void cleanup() {
        _bolt.cleanup();
    }
    public Map<String, Object> getComponentConfiguration() {
        return _bolt.getComponentConfiguration();
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值