Spout

本文深入探讨了Apache Storm中Spout接口的设计与实现细节,包括ISpout和IRichSpout的功能区别,以及如何通过实现nextTuple、ack和fail等方法确保消息的可靠传输。文章还提供了一个具体的银行交易事务处理案例,演示如何利用这些方法实现消息重传机制。

转载自:https://blog.youkuaiyun.com/lulongzhou_llz/article/details/46399445

Spout 接口

  Storm中与Spout相关的接口主要有ISpoutIRichSpout
  Spout中由于nextTupleackfail方法是在一个线程里面被调用的,如果nextTuple阻塞,其他方法也将被阻塞,这样会有许多意外情况发生,因此nextTuple必须是非阻塞的。任何的Spout都将利用nextTuple来发送信息。

  ISpout的failack回调方法仅仅给出了发送消息时所对应的Messageld, 而没有给出具体的消息内容,这就意味着如果要实现消息的重传,用户则需要自己来维护那些已经发送的消息。
  实现Spout的过程中,用户可以编写其构造函数,然而该构造函数并不会被实际调用。因为在提交Topology时,系统会调用Topology的构造函数(而非Spout的构造函数 ),并将产生的对象序列化成字符数组。每一个节点上的Spout对象都是通过反序列化得到的,这可能导致某些成员没有被正确初始化。ISpout中的open回调函数会在对象被反序列后调用,我们应当在open方法中对对象的复杂成员进行初始化,而不应使用构造函数来完成这一过程。
  IRichSpout同时实现IComponent和ISpout接口,于是从含义上看它表示一个具有Spout功能的组件。


  对于Storm来说,根据每个拓扑的需要担保消息的可靠性是开发者的责任。这就涉及到消息可靠性和资源消耗之间的权衡。高可靠性的拓扑必须管理丢失的消息,必然消耗更多资源;可靠性较低的拓扑可能会丢失一些消息,占用的资源也相应更少。不论选择什么样的可靠性策略,Storm都提供了不同的工具来实现它。
  要在spout中管理可靠性,可以在分发时包含一个元组的消息ID(collector.emit(new Values(…),tupleId))。在一个元组被正确的处理时调用ack方法,而在失败时调用fail方法。当一个元组被所有的靶bolt和锚bolt处理过,即可判定元组处理成功。

  发生下列情况之一时为元组处理失败:

  • 提供数据的spout调用collector.fail(tuple)
  • 处理时间超过配置的超时时间

  来看一个例子。处理银行事务,需求如下:如果事务失败了,重新发送消息;如果失败了太多次,终结拓扑运行。
  创建一个spout和一个bolt,spout随机发送100个事务ID,有80%的元组不会被bolt收到。实现spout时利用Map分发事务消息元组,这样就比较容易实现重发消息。

public void nextTuple() {
    if(!toSend.isEmpty()){
        for(Map.Entry<Integer, String> transactionEntry : toSend.entrySet()){
            Integer transactionId = transactionEntry.getKey();
            String transactionMessage = transactionEntry.getValue();
            collector.emit(new Values(transactionMessage),transactionId);
        }
        toSend.clear();
    }
}

  如果有未发送的消息,得到每条事务消息和它的关联ID,把它们作为一个元组发送出去,最后清空消息队列。值得一提的是,调用mapclear是安全的,因为nextTuple失败时,只有ack方法会修改map,而它们都运行在一个线程内。

  维护两个map用来跟踪待发送的事务消息和每个事务的失败次数。ack方法只是简单的把事务从每个列表中删除。

public void ack(Object msgId) {
    messages.remove(msgId);
    failCounterMessages.remove(msgId);
}

  fail方法决定应该重新发送一条消息,还是已经失败太多次而放弃它。

  NOTE:如果你使用全部数据流组,而拓扑里的所有bolt都失败了,spoutfail方法才会被调用。

public void fail(Object msgId) {
    Integer transactionId = (Integer) msgId;
    //检查事务失败次数
    Integer failures = transactionFailureCount.get(transactionId) + 1;

    if(failes >= MAX_FAILS){
        //失败数太高了,终止拓扑
        throw new RuntimeException("错误, transaction id 【"+
         transactionId+"】 已失败太多次了 【"+failures+"】");
    }

    //失败次数没有达到最大数,保存这个数字并重发此消息
    transactionFailureCount.put(transactionId, failures);
    toSend.put(transactionId, messages.get(transactionId));
    LOG.info("重发消息【"+msgId+"】");
}

  首先,检查事务失败次数。如果一个事务失败次数太多,通过抛出RuntimeException终止发送此条消息。否则,保存失败次数,并把消息放入待发送队列(toSend),它就会再次调用nextTuple时得以重新发送。

  NOTE:Storm节点不维护状态,因此如果在内存保存信息(就像本例做的那样),而节点又不幸挂了,就会丢失所有缓存的消息。
  Storm是一个快速失败的系统。拓扑会在抛出异常时挂掉,然后再由Storm重启,恢复到抛出异常前的状态。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值