天天看点

storm的ack原理

    1.  Storm的ack机制
      1. Ack机制基本知识

(1)为了保证消息处理过程中的可靠性,storm使用了ack机制。storm会专门启动若干acker线程,来追踪tuple的处理过程。acker线程数量可以设置。

(2)每一个Tuple在Spout中生成的时候,都会分配一个64位的messageId。通过对messageId进行哈希我们可以知道要对哪个acker线程发送消息来通知它监听这个Tuple。

(3)acker线程收到消息后,会将发出消息的Spout和那个messageId绑定起来。然后开始跟踪该tuple的处理流程。

(4)如果这个tuple全部都处理完,那么acker线程就会调用发起这个tuple的那个spout实例的ack()方法。如果超过一定时间这个tuple还没处理完,那么acker线程就会调用对应spout的fail()方法,通知spout消息处理失败。spout组件就可以重新发送这个tuple。

(5)从上面的介绍我们知道了,tuple数据的流向会形成一个拓扑图,也可以理解成是一个tuple树。这个拓扑图的节点可能会有很多个,如果要把这些节点全部保存起来,处理大量的数据时势必会造成内存溢出。对于这个难题,storm使用了一种非常巧妙的方法,使用20个字节就可以追踪一个tuple是否被完整的执行。这也是storm的一个突破性的技术。

      1. Ack机制基本思想

(1)对于从Spout发射出来的每个spout tuple,Acker都保存了一个ack-val(校验值),初始值为0

(2)每当tuple被创建或被ack,这些对应tuple的tuple-id(随机生成的64位整数)都会在某个时刻和保存的ack-val进行按位异或运算,并用异或运算的结果更新ack-val。

(3)如果每个spout tuple对应tuple树中的每个tuple都被成功处理,那最终的ack-val必然为0。

因为在这个过程中,同一个tuple-id都会被异或两次,而相同值的异或运算结果为0,且异或运算满足结合律,如a^a=0,a^b^a^b=(a^a)^(b^b)=0

      1. Ack机制具体原理
        1. Ack-val如何更新
storm的ack原理
storm的ack原理

Acker跟踪算法需要不断更新ack-val,那ack-val又是怎么更新?主要就是如下3个环节

(1)Spout创建新tuple给Acker发送消息

(2)bolt中tuple被ack的时候给Acker发送消息

(3)Acker根据接收到的消息做异或运算,更新自己的ack-val

        1. Acker维护的数据结构
          1. Acer维护的数据结构

Acker为了实现自己的跟踪算法,它会维护这样一个数据结构:

{root-id{:spout-task task-id  :val ack-val  :failed bool-val …}}

其实就是一个Map,从上面这个Map中,我们知道,一个Acker存储了一个root-id到一对值的映射关系。这对值的第一个是创建这个tuple的task-id,当这个tuple处理完成进行ack的时候会用到。第二个是一个随机的64位的数字,即ack-val,ack-val表示整棵tuple树的状态,不管这棵tuple树多大,它只是简单地把这棵树上的相应的tuple-id做按位异或运算。因此即使一个spout tuple生成一棵有成千上万tuple的tuple树,Acker进行跟踪时也不会耗费太多的内存,对于每个spout tuple,Acker所需要的内存量都是恒定的20字节。这也是Storm的主要突破。

          1. Acker的数据结构中各个id用处

(1)Spout-id/root-id:

a)spout-id也称为root-id(传递给这个spout生成的tuple树中的每个tuple,有了root-id就可以跟踪这个tuple树)。

b)通过root-id的mod hashing映射到相应的Acker。Acker有很多

(2)Task-id:spout tuple被完全处理后,会调用Spout中的task的ack或者fail方法,Acker通过Task-id通知spout tuple对应的Task。

          1. Spout和Bolt向Acker发送数据的不同
storm的ack原理

(1)Spout:消息的格式为[root-id,tmp-ack-val,task-id]

(2)Bolt:消息格式为[root-id,tmp-ack-val]

        1. ack实例
storm的ack原理

(1)0010和1011是spout产生的两个tuple-id,0001是root-id。产生的tuple-id先进行异或得到1001,再和Acker中的ack-val即0进行异或得到1001

(2)0010是Bolt1的输入tuple-id,产生的tuple-id是0110。这两个值进行异或得到0100,再和Acker的ack-val即1001进行异或得到1101

(3)(4)一样。