storm中的ack-fail机制

概念:storm的ack-fail机制也就是storm的可靠消息处理机制,通俗来讲就是给spout发出的每个tuple带上一个messageid,然后这个spout下面的每一个bolt
都会给他返回一个完成情况,只有当每一个bolt都返回了正确的结果,整个发送过程才算成功,任何一个bolt处理不成功,则不成功。

我对ack-fail机制的讲解分为三个层面:分别是api应用也就是写代码方面、ack-fail机制的处理过程。

首先是代码编写方面:
假设我们在这个系统中有一种spout和一种bolt,如果你不使用ack-fail机制那么一个spout中有三个方法,分别是open(),nextTuple()和outputFields(),
open的作用是初始化那个outputCollector,nextTuple方法就是不断地取值然后发给下一个bolt或者就结束了,declareOutputFields方法就是声明一下我发
射出去的数据id,如果你使用了ack-fail机制那就多了俩方法,ack()和fail(),发送成功了就调ack方法,不成功就调fail,你在fail里可以进行重发或者什
么的,当然这些都是你自己决定,你要是不想做处理函数里就啥都不写,我们这里进行重发:
Myspout{
    Map<String,Values> buffer = new HashMap<>();  //缓存正在发送的tuple
    open();
    nextTuple(){
        String messageId = UUID.randomUUID().toString().replace("-", "");  //随机生成一个msgid
        buffer.put(messageId,tuple);  //放到缓存中
        collector.emit(value,messageId); //发射出去
    }
    fail(Object msgid){
        String value = buffer.get(msgid);  //取出value
        collector.emit(value,msgid);  //重发
    }
    ack(Object msgid){
        buffer.remove(msgid);  //从buffer中拿出来
    }
    declareOutputFields();
}
MyBolt方法本来的三个方法是prepare(),execute(),declareOutputFields(),prepare方法主要也就是初始化那个outputCollector,execute方法就是执行处理
过程,declareOutputFields也一样就是声明一下我发出去的是啥,而在应用了ack-fail机制的bolt中,这里要显示的声明我处理完了:
MyBolt{
    void execute(Tuple input){
        collector.emit(input,value);
        collector.ack(input);
    }
}

然后是ack-fail的处理过程方面:
spout---->tuple1---->bolt1---->ack(tuple1)
                     bolt1---->tuple1-1---->bolt2-1---->ack(tuple1-1);
                     bolt1---->tuple1-2---->bolt2-1---->ack(tuple1-2);
                                            ..........................
只有当每一个bolt都正确ack了,整个发送过程才算成功,任何一个bolt处理不成功,则不成功,重新处理。                                    
那么ack这个东西他如何判断前者发射的tuple和ack返回的tuple是不是同一个呢,这里主要的概念是异或处理,对于每一个spout发射任务,ack维护了这样
一组数据,<spoutTaskId,<RootID,ackValue>,spoutTaskId标志着唯一的一个spouttask,RootId标志着整个过程的结果,ackValue记录着整个过程中不同
的tuple相异或的时候结果的变化,当ackValue最终等于0的时候,就标志着整个过程成功了,那么这个RootID如何计算呢,我们知道每一个tuple的发射过
程bolt都给了相应的返回tupleid,当这两个tupleid相同时就表明这一小阶段的任务完成了,而tuplid转化成二进制是0101形式的,如果返回的tupleid和
这个发送的tupleid相异或等于0,也就是ackValue等于0,就证明这两个是同一个id,也就表明这一小部分的任务成功了,但是整个过程中可能会有多层bolt,
每一个bolt的执行速度可能不同,所以注意,如果这些所有结果相异或后,ackValue等于0,就表明这个传输任务完成了。

最后我们来从底层实现来讲一下ack-fail机制:
我们运行storm程序时会发现有这样一个任务-ackTask,看一下源码他是继承了Bolt,他就是一个和其他数据处理的bolt一起存在一起处理的进程,而实际上
整个过程中是存在两种tuple的,分别是DataTuple和AckTuple,DataTuple主要负责数据的处理,AckTuple负责整个过程的排错,我们先来看这个AckTuple,
他其实封装了AckTuple<RootID,tupleId>,RootID标识了这个tuple属于哪个过程,而tupleId标识了每一个特定的tuple,这个AckTuple最终封装成一个
messageId这样一个对象,而DataTuple中就含有这个messageId。接下来我们来看整个过程,两种线程是一起进行的,ack的线程比较简单,当spout发射
一个DataTuple时同时就会发射一个AckTuple,然后他就在这等待响应,spout将DataTuple(messageId(AckTuple))发送给bolt,bolt.execute(dataTuple)
之后会应答也就是bolt.ack(dataTuple),而dataTuple中封装了ackTuple,就可以还原出这个ackTuple,这样acktask就等到了ack应答,也就是说这一阶段
处理成功,以此类推。

上述过程示意:
spout.emit(dataTuple(messageId(ackTuple)))--->bolt.excute--->bolt.ack(dataTuple(ackTuple))
spout.emit(ackTuple)

这就是我理解的整个ack-fail机制

猜你喜欢

转载自blog.csdn.net/qq_41571974/article/details/81633639
今日推荐