Twitter Storm如何保证消息不丢失

作者:  xumingming | 可以转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明
网址:  http://xumingming.sinaapp.com/127/twitter-storm如何保证消息不丢失/

 
本文翻译自: https://github.com/nathanmarz/storm/wiki/Guaranteeing-message-processing

storm保证从spout发出的每个tuple都会被完全处理。这篇文章介绍storm是怎么做到这个保证的,以及我们使用者怎么做才能充分利用storm的可靠性特点。

一个tuple被”完全处理”是什么意思?

就如同蝴蝶效应一样,从spout发射的一个tuple可以引起其它成千上万个tuple因它而产生, 想想那个计算一篇文章中每个单词出现次数的topology.

1
2
3
4
5
6
7
8
9
TopologyBuilder builder = new TopologyBuilder();
builder.setSpout( 1 , new KestrelSpout( "kestrel.backtype.com" ,
                                      22133 ,
                                      "sentence_queue" ,
                                      new StringScheme()));
builder.setBolt( 2 , new SplitSentence(), 10 )
         .shuffleGrouping( 1 );
builder.setBolt( 3 , new WordCount(), 20 )
         .fieldsGrouping( 2 , new Fields( "word" ));

这个topology从一个Kestrel队列读取句子, 把每个句子分割成一个个单词, 然后发射这一个个单词: 一个源tuple(一个句子)引起后面很多tuple的产生(一个个单词), 这个消息流大概是这样的:

统计单词出现次数的tuple树

统计单词出现次数的tuple树

在storm里面一个tuple被完全处理的意思是: 这个tuple以及由这个tuple所导致的所有的tuple都被成功处理。而一个tuple会被认为处理失败了如果这个消息在timeout所指定的时间内没有成功处理。 而这个timetout可以通过Config.TOPOLOGY_MESSAGE_TIMEOUT_SECS来指定。

 

如果一个消息处理成功了或者失败了会发生什么?

FYI。 下面这个是spout要实现的接口:

1
2
3
4
5
6
7
8
public interface ISpout extends Serializable {
     void open(Map conf, TopologyContext context,
               SpoutOutputCollector collector);
     void close();
     void nextTuple();
     void ack(Object msgId);
     void fail(Object msgId);
}

首先storm通过调用spout的nextTuple方法来获取下一个tuple, Spout通过open方法参数里面提供的SpoutOutputCollector来发射新tuple到它的其中一个输出消息流, 发射tuple的时候spout会提供一个message-id, 后面我们通过这个message-id来追踪这个tuple。举例来说, KestrelSpout从kestrel队列里面读取一个消息,并且把kestrel提供的消息id作为message-id, 看例子:

1
_collector.emit( new Values( "field1" , "field2" , 3 ),msgId);

接下来, 这个发射的tuple被传送到消息处理者bolt那里, storm会跟踪由此所产生的这课tuple树。如果storm检测到一个tuple被完全处理了, 那么storm会以最开始的那个message-id作为参数去调用消息源的ack方法;反之storm会调用spout的fail方法。值得注意的一点是, storm调用ack或者fail的task始终是产生这个tuple的那个task。所以如果一个spout被分成很多个task来执行, 消息执行的成功失败与否始终会通知最开始发出tuple的那个task。

我们再以KestrelSpout为例来看看spout需要做些什么才能保证“一个消息始终被完全处理”, 当KestrelSpout从Kestrel里面读出一条消息, 首先它“打开”这条消息, 这意味着这条消息还在kestrel队列里面, 不过这条消息会被标示成“处理中”直到ack或者fail被调用。处于“处理中“状态的消息不会被发给其他消息处理者了;并且如果这个spout“断线”了, 那么所有处于“处理中”状态的消息会被重新标示成“等待处理”.

Storm的可靠性API

作为storm的使用者,有两件事情要做以更好的利用storm的可靠性特征。 首先,在你生成一个新的tuple的时候要通知storm; 其次,完成处理一个tuple之后要通知storm。 这样storm就可以检测整个tuple树有没有完成处理,并且通知源spout处理结果。storm提供了一些简洁的api来做这些事情。

由一个tuple产生一个新的tuple称为: anchoring。你发射一个新tuple的同时也就完成了一次anchoring。看下面这个例子: 这个bolt把一个包含一个句子的tuple分割成每个单词一个tuple。

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SplitSentence implements IRichBolt {
         OutputCollector _collector;
 
         public void prepare(Map conf,
                             TopologyContext context,
                             OutputCollector collector) {
             _collector = collector;
         }
 
         public void execute(Tuple tuple) {
             String sentence = tuple.getString( 0 );
             for (String word: sentence.split( " " )) {
                 _collector.emit(tuple, new Values(word));
             }
             _collector.ack(tuple);
         }
 
         public void cleanup() {
         }
 
         public void declareOutputFields(OutputFieldsDeclarer declarer) {
             declarer.declare( new Fields( "word" ));
         }
     }

看一下这个execute方法, emit的第一个参数是输入tuple, 第二个参数则是输出tuple, 这其实就是通过输入tuple anchoring了一个新的输出tuple。因为这个“单词tuple”被anchoring在“句子tuple”一起, 如果其中一个单词处理出错,那么这整个句子会被重新处理。作为对比, 我们看看如果通过下面这行代码来发射一个新的tuple的话会有什么结果。

1
_collector.emit( new Values(word));

用这种方法发射会导致新发射的这个tuple脱离原来的tuple树(unanchoring), 如果这个tuple处理失败了, 整个句子不会被重新处理。到底要anchoring还是要 unanchoring则完全取决于你的业务需求。

一个输出tuple可以被anchoring到多个输入tuple。这种方式在stream合并或者stream聚合的时候很有用。一个多入口tuple处理失败的话,那么它对应的所有输入tuple都要重新执行。看看下面演示怎么指定多个输入tuple:

1
2
3
4
List<Tuple> anchors = new ArrayList<Tuple>();
anchors.add(tuple1);
anchors.add(tuple2);
_collector.emit(anchors, new Values( 1 , 2 , 3 ));

多入口tuple把这个新tuple加到了多个tuple树里面去了。

我们通过anchoring来构造这个tuple树,最后一件要做的事情是在你处理完当个tuple的时候告诉storm,  通过OutputCollector类的ack和fail方法来做,如果你回过头来看看SplitSentence的例子, 你可以看到“句子tuple”在所有“单词tuple”被发出之后调用了ack。

你可以调用OutputCollector 的fail方法去立即将从消息源头发出的那个tuple标记为fail, 比如你查询了数据库,发现一个错误,你可以马上fail那个输入tuple, 这样可以让这个tuple被快速的重新处理, 因为你不需要等那个timeout时间来让它自动fail。

每个你处理的tuple, 必须被ack或者fail。因为storm追踪每个tuple要占用内存。所以如果你不ack/fail每一个tuple, 那么最终你会看到OutOfMemory错误。

大多数Bolt遵循这样的规律:读取一个tuple;发射一些新的tuple;在execute的结束的时候ack这个tuple。这些Bolt往往是一些过滤器或者简单函数。Storm为这类规律封装了一个BasicBolt类。如果用BasicBolt来做, 上面那个SplitSentence可以改写成这样:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
public class SplitSentence implements IBasicBolt {
         public void prepare(Map conf,
                             TopologyContext context) {
         }
 
         public void execute(Tuple tuple,
                             BasicOutputCollector collector) {
             String sentence = tuple.getString( 0 );
             for (String word: sentence.split( " " )) {
                 collector.emit( new Values(word));
             }
         }
 
         public void cleanup() {
         }
 
         public void declareOutputFields(
                         OutputFieldsDeclarer declarer) {
             declarer.declare( new Fields( "word" ));
         }
     }

这个实现比之前的实现简单多了, 但是功能上是一样的。发送到BasicOutputCollector的tuple会自动和输入tuple相关联,而在execute方法结束的时候那个输入tuple会被自动ack的。
作为对比,处理聚合和合并的bolt往往要处理一大堆的tuple之后才能被ack, 而这类tuple通常都是多输入的tuple, 所以这个已经不是IBasicBolt可以罩得住的了。

storm是怎么实现高效率的可靠性的?

storm里面有一类特殊的task称为:acker, 他们负责跟踪spout发出的每一个tuple的tuple树。当acker发现一个tuple树已经处理完成了。它会发送一个消息给产生这个tuple的那个task。你可以通过Config.TOPOLOGY_ACKERS来设置一个topology里面的acker的数量, 默认值是一。 如果你的topology里面的tuple比较多的话, 那么把acker的数量设置多一点,效率会高一点。

理解storm的可靠性的最好的方法是来看看tuple和tuple树的生命周期, 当一个tuple被创建, 不管是spout还是bolt创建的, 它会被赋予一个64位的id,而acker就是利用这个id去跟踪所有的tuple的。每个tuple知道它的祖宗的id(从spout发出来的那个tuple的id), 每当你新发射一个tuple, 它的祖宗id都会传给这个新的tuple。所以当一个tuple被ack的时候,它会发一个消息给acker,告诉它这个tuple树发生了怎么样的变化。具体来说就是:它告诉acker: 我呢已经完成了, 我有这些儿子tuple, 你跟踪一下他们吧。下面这个图演示了C被ack了之后,这个tuple树所发生的变化。

tuple ack示例

tuple ack示例

关于storm怎么跟踪tuple还有一些细节, 前面已经提到过了, 你可以自己设定你的topology里面有多少个acker。而这又给我们带来一个问题, 当一个tuple需要ack的时候,它到底选择哪个acker来发送这个信息呢?

storm使用一致性哈希来把一个spout-tuple-id对应到acker, 因为每一个tuple知道它所有的祖宗的tuple-id, 所以它自然可以算出要通知哪个acker来ack。(这里所有的祖宗是指这个tuple所对应的所有的根tuple。这里注意因为一个tuple可能存在于多个tuple树,所以才有所有一说)。

storm的另一个细节是acker是怎么知道每一个spout tuple应该交给哪个task来处理。当一个spout发射一个新的tuple, 它会简单的发一个消息给一个合适的acker,并且告诉acker它自己的id(taskid), 这样storm就有了taskid-tupleid的对应关系。 当acker发现一个树完成处理了, 它知道给哪个task发送成功的消息。

acker task并不显式的跟踪tuple树。对于那些有成千上万个节点的tuple树,把这么多的tuple信息都跟踪起来会耗费太多的内存。相反, acker用了一种不同的方式, 使得对于每个spout tuple所需要的内存量是恒定的(20 bytes) .  这个跟踪算法是storm如何工作的关键,并且也是它的主要突破。

一个acker task存储了一个spout-tuple-id到一对值的一个mapping。这个对子的第一个值是创建这个tuple的taskid, 这个是用来在完成处理tuple的时候发送消息用的。 第二个值是一个64位的数字称作:”ack val”, ack val是整个tuple树的状态的一个表示,不管这棵树多大。它只是简单地把这棵树上的所有创建的tupleid/ack的tupleid一起异或(XOR)。

当一个acker task 发现一个 ack val变成0了, 它知道这棵树已经处理完成了。 因为tupleid是随机的64位数字, 所以, ack val碰巧变成0(而不是因为所有创建的tuple都完成了)的几率极小。算一下就知道了, 就算每秒发生10000个ack, 那么需要50000000万年才可能碰到一个错误。而且就算碰到了一个错误, 也只有在这个tuple失败的时候才会造成数据丢失。 关于Acker的详细工作流程的分析可以看看这篇文章: Twitter Storm源代码分析之acker工作流程

既然你已经理解了storm的可靠性算法, 让我们一起过一遍所有可能的失败场景,并看看storm在每种情况下是怎么避免数据丢失的。

1. 由于对应的task挂掉了,一个tuple没有被ack: storm的超时机制在超时之后会把这个tuple标记为失败,从而可以重新处理。

2. Acker挂掉了: 这种情况下由这个acker所跟踪的所有spout tuple都会超时,也就会被重新处理。

3. Spout挂掉了: 在这种情况下给spout发送消息的消息源负责重新发送这些消息。比如Kestrel和RabbitMQ在一个客户端断开之后会把所有”处理中“的消息放回队列。

就像你看到的那样, storm的可靠性机制是完全分布式的, 可伸缩的并且是高度容错的。

 

调整可靠性 (Tuning Reliability)

acker task是非常轻量级的, 所以一个topology里面不需要很多acker。你可以通过Strom UI(id: -1)来跟踪它的性能。 如果它的吞吐量看起来不正常,那么你就需要多加点acker了。

如果可靠性对你来说不是那么重要 — 你不太在意在一些失败的情况下损失一些数据, 那么你可以通过不跟踪这些tuple树来获取更好的性能。不去跟踪消息的话会使得系统里面的消息数量减少一半, 因为对于每一个tuple都要发送一个ack消息。并且它需要更少的id来保存下游的tuple, 减少带宽占用。

有三种方法可以去掉可靠性。第一是把Config.TOPOLOGY_ACKERS 设置成 0. 在这种情况下, storm会在spout发射一个tuple之后马上调用spout的ack方法。也就是说这个tuple树不会被跟踪。

第二个方法是在tuple层面去掉可靠性。 你可以在发射tuple的时候不指定messageid来达到不跟粽某个特定的spout tuple的目的。

最后一个方法是如果你对于一个tuple树里面的某一部分到底成不成功不是很关心,那么可以在发射这些tuple的时候unanchor它们。 这样这些tuple就不在tuple树里面, 也就不会被跟踪了。

此条目发表在  storm 分类目录,贴了  stormtwitter 标签。将 固定链接加入收藏夹。

Twitter Storm如何保证消息不丢失》有 46 条评论

  1. Pingback 引用通告: Twitter Storm: 在生产集群上运行topology | 徐明明

  2. Pingback 引用通告: Twitter Storm: storm的一些常见模式 | 徐明明

  3. fiw  说:

    storm为了保证可靠性处理是否必须要存储还没有完全处理的Turple?这样发送Turple的Spout是否会出现OOM?

    • xumingming  说:

      文章中已经提到了:

      每个你处理的tuple, 必须被ack或者fail。因为storm追踪每个tuple要占用内存。所以如果你不ack/fail每一个tuple, 那么最终你会看到OutOfMemory错误。

      所以我们要做的是快速告诉storm我们对消息的处理结果(ack或者fail).

      • maoxiangyi  说:

        也就是说,在execute的方法中,如果方法体正常执行,就立马调用ack方法。执行时发生异常,即调用fail方法。如此可以避免由Storm框架自行检测浪费更多的时间和性能。是这样的吗?

  4. LiuShaohui  说:

    是否有相关的性能报告或者benchmark,storm的性能达到多少?

  5. Pingback 引用通告: Twitter Storm源代码分析之acker工作流程 | 徐明明

  6. Pingback 引用通告: Twitter Storm入门 | 徐明明

  7. gembler  说:

    对于一些没有ack或fail的queue,例如redis的pub sub,spout的fail被调用的时候,只拿到一个id,却拿不到msg的内容。有办法拿回dequeue出来原始的msg吗?或者tuple。

    • xumingming  说:

      不行, 你的代码要能够根据storm的tuple-id, 重新发射这个tuple。比如把没ack的tuple放在一个list里面,处理完了,从list里面清除。

  8. bohr.qiu  说:

    1. 由于对应的task挂掉了,一个tuple没有被ack: storm的超时机制在超时之后会把这个tuple标记为失败,从而可以重新处理。

    2. Acker挂掉了: 这种情况下由这个acker所跟踪的所有spout tuple都会超时,也就会被重新处理。

    —————————————————————————-
    楼主,以上两点这样理解我觉得才对:
    1、在tuple超时时候,发射tuple的那个spout回被回调fail方法。
    2、你需要自己来编写逻辑来重新处理fail掉的tuple,比如放入一个列表,在nextTuple()中获取这些失败的tuple在发射出去。而不是说storm会自动帮你重新发射tuple。
    对吗?

  9. Pingback 引用通告: Twitter Storm: Transactional Topolgoy简介 | 徐明明

  10. macrochen  说:

    “每个你处理的tuple, 必须被ack或者fail。因为storm追踪每个tuple要占用内存。所以如果你不ack/fail每一个tuple, 那么最终你会看到OutOfMemory错误。”

    既然有这种问题, 为什么storm在最初设计的时候, 不采用每个bolt执行完之后内部主动去调用collector.ack, 应用方只需要根据逻辑来决定是否需要调用fail即可.

    • xumingming  说:

      自动ack之后还怎么fail呢? ack表示这个tuple已经“成功了“, 内存里面就不再对了进行追踪了, 你后面还怎么”fail”呢?应该是不行的。

  11. macrochen  说:

    “每个你处理的tuple, 必须被ack或者fail。因为storm追踪每个tuple要占用内存。所以如果你不ack/fail每一个tuple, 那么最终你会看到OutOfMemory错误。”

    既然有这种问题, 为什么storm在最初设计的时候, 不采用每个bolt执行完之后内部主动去调用collector.ack, 应用方只需要根据逻辑来决定是否需要调用fail即可.

  12. Tony  说:

    不知道ack或者fail之后,storm会进行哪些操作,源码里没有看到相关GC的动作,怎么就能防止OOM了呢?

    • xumingming  说:

      GC的动作?你是指主动触发GC? ack之后storm是会从一个map里面把ack过的对象干掉的,但是理论上如果你发的ack很多,确实存在OOM的可能。你可以通过设置max_spout_pending来调整系统内未ack/fail的 tuple数量。

  13. silentdai  说:

    “一个acker task存储了一个spout-tuple-id到一对值的一个mapping。这个对子的第一个值是创建这个tuple的taskid, 这个是用来在完成处理tuple的时候发送消息用的。 第二个值是一个64位的数字称作:”ack val”

    请问ack val是怎样产生的呢?可以手动控制的吗?
    因为我有个场景,在重放某个tuple的时候不能保证一致(比如我要在tuple记录重放时的时刻),但其实对下游的bolt来说,它关心的是除时间戳以外的域;因此希望这个ack val和tuple中的时间戳域无关。
    形式化一点,就是#tuple{content=XX, timestamp=TT}, timestamp记录nextTuple()的时刻,如果这个tuple超时,我希望tuple被重放的时候重放的是{content=XX,timestamp=NEWTT}。能不能对这个只关心content=XX的tuple达到exactly-once的结果?

  14. jennia  说:

    xumingming 您好!
    请教一下,我们同一个spout源数据有多个业务处理场景,比如一个bolt直接进行存储数据到DB,另几个bolt进行不同的统计计算,当一个bolt宕掉后,其它bolt因为已经将数据存储到DB了,所以不想重复处理数据,又不想和数据库进行频繁交互有什么好的方式实现么?数据库我们使用mongoDB,当数据量大到一定程度后insert+update效率非常差

  15. WIlls.tan  说:

    楼主,我自己写的storm job在Storm UI上报了很多fail而且fail的Tuple远远大于正常处理的Tuple。从未遇到过fail的情况,找不到原因。Bolt我用的IBasicBolt,请问能给我举一个发生fail的Topology的例子么?不胜感激。

  16. 陈小召  说:

    1、由于对应的task挂掉了,一个tuple没有被ack: storm的超时机制在超时之后会把这个tuple标记为失败,从而可以重新处理。
    这个对于那种一个bolt, fieldsGrouping 接受tuple的情况,比如wordcount那个bolt,假如bolt用2个任务来执行(暂定叫a任务,和b任务吧),一个单词比如 cat,总是会流到a任务中执行,然后记录cat的数量,但是如果a任务死了,正在流的一个“cat” 就要重新发送,这时候会发送b任务吗? 如果发送到了b任务,哪cat的数量计数不就错误了吗?

  17. Pingback 引用通告: storm 原理简介及单机版安装指南(转) | 盛大创新院团队博客 | 数据,更懂人心

  18. habren  说:

    1.acker是会判断从源spout到最终bolt这整条线是否成功。如果失败了,它会告诉源spout吗?2.如果问题1的答案是“是”,那么spout哪个接口能知道这一反馈,从而重新发送,还是说storm自己实现了让spout重新emit这一tuple?3.如果一个topology中有一个spout和两个bolt,bolt1和bolt2.spout emit tuple1后就ack,bolt1 emit tuple2后也ack。到了bolt2,处理时发生exception,它调了fail。这时bolt1会知道刚刚那个tuple2处理不成功,然后重新emit一次这个tuple2吗?如果是,那它是怎么知道tuple2没处理成功的?而且仅仅只是bolt1重新emit tuple2还是要从spout开始,由它重新emit tuple1,然后由bolt1重新emit tuple2?如果是后者,那bolt1里对tuple2的操作岂不是要做两遍,出现重复处理?

  19. Lichuan  说:

    问个问题:acker这个task是运行在控制节点上呢,还是运行在工作节点上?如何判断一个task运行在哪类节点上?

  20. fubupc  说:

    你好,有个疑问:bolt在anchoring接受到的tuple时,是不是会向相应的acker发送一个消息也就是文中说的“首先,在你生成一个新的tuple的时候要通知storm。”?

  21. fubupc  说:

    你好,有个疑问:bolt在anchoring接受到的tuple时,是不是会向相应的acker发送一个消息也就是文中说的“首先,在你生成一个新的tuple的时候要通知storm。”?

  22. juliali  说:

    “它只是简单地把这棵树上的所有创建的tupleid/ack的tupleid一起异或(XOR)。” 这句话这样讲稍微有点令人费解,我就是搜了其他资料才看明白得。
    能否把这句话和下段前两句话一起改成这样:

    “它只是简单得把这棵树上所有被创建以及被ack的tuple的tupleid一起异或(XOR)。考虑到tupleid是随机的64位数字, ack val碰巧变成0的几率极小。在排除掉ack val因为巧合为0的情况之后,只有在所有的tuple都经过了创建和ack这两步操作之后,也就是说每一个tupleid都在XOR算式中出现两次之后,ack val才会为0。因此,当一个acker task 发现一个 ack val变成0了, 它知道这棵树已经处理完成了。

    • juliali  说:

      还应该再加一句:“当tuple树中只有根节点存在时,计算ack val的时候,根tuple的tupleid和0进行XOR计算。”

  23. juliali  说:

    有问题请教一下。

    在tuple层面去掉可靠性,是说在某些bolt 发射tuple的时候,不将新生成的tuple与其父tuple anchor在一起,这样,新生成的tuple就不在tuple树里。是这样吧?

    这就是说,只有spout发射的tuple才能成为一棵tuple树的根节点,而blot发射的tuple,如果被unanchor了,就将被acker忽略,而不能以其为根再建一颗新的tuple树。我这样理解对吗?

    • fubupc  说:

      应该是对的。

      另外,我做了一些测试,感觉应该不是用tuple id来计算的。一个spout发射一个tuple给bolt的时候,带了两个ID,一个是tuple id,另外一个才是用于异或计算的值。

      个人猜测应该是用来处理一个spout直接连接多个bolt的情况。比如:
      spout — bolt-1
      |
      bolt-2
      如果采用tuple id。那只要有一个bolt向acker确认了,ack val 就为0了。而实际上这时整个tuple树还没处理完。

      根据测试看来,spout发射tuple的时候,会做几件事:
      1. 生成一个随机tuple id
      2. 为每一个下游bolt生成一个专门的随机值,再加上1中的tuple id一起发给相应bolt
      3. 把2中为每个bolt生成的随机值进行异或计算,得到的结果 + tuple id + 任务编号一起发送给acker

  24. Pingback 引用通告: Storm基础

  25. Joey  说:

    你好,我初看storm一周多点时间,我想问下,我有一点不明白,假如我从spout A发出一个tuple到bolt B,该bolt把该tuple转换成多个tuples,分发给多个bolts C,D,E,我的问题是如果E输出tuple失败,那么spout A会重新发送一开始的整个未被拆分的tuple,重新发送运行吗?还是通知失败bolt 的父bolt重新发送相应的tuple呐???

  26. Joey  说:

    你好,我初看storm一周多点时间,我想问下,我有一点不明白,假如我从spout A发出一个tuple到bolt B,该bolt把该tuple转换成多个tuples,分发给多个bolts C,D,E,我的问题是如果E输出tuple失败,那么spout A会重新发送一开始的整个未被拆分的tuple,重新发送运行吗?还是通知失败bolt 的父bolt重新发送相应的tuple呐???

    • 泣尘  说:

      看下面这段话:

      理解storm的可靠性的最好的方法是来看看tuple和tuple树的生命周期, 当一个tuple被创建, 不管是spout还是bolt创建的, 它会被赋予一个64位的id,而acker就是利用这个id去跟踪所有的tuple的。每个tuple知道它的祖宗的id(从spout发出来的那个tuple的id), 每当你新发射一个tuple, 它的祖宗id都会传给这个新的tuple。所以当一个tuple被ack的时候,它会发一个消息给acker,告诉它这个tuple树发生了怎么样的变化。

      从上面这段话应该可以看出,每个tuple里面保存的id都是你说的spoutA发出的那个tuple的id;如你说的E发送失败,应该会调用fail方法的吧,那么传入的id值应该就是前面说的那个id,即spoutA发出的tuple的id,所以应该是从头重新发送了吧。

      我个人的理解,不知道到底是不是这么回事,也是刚看storm一周多,没有做过什么测试,所以都是猜想,希望可以多多交流。

    • 泣尘  说:

      看下面这段话:

      理解storm的可靠性的最好的方法是来看看tuple和tuple树的生命周期, 当一个tuple被创建, 不管是spout还是bolt创建的, 它会被赋予一个64位的id,而acker就是利用这个id去跟踪所有的tuple的。每个tuple知道它的祖宗的id(从spout发出来的那个tuple的id), 每当你新发射一个tuple, 它的祖宗id都会传给这个新的tuple。所以当一个tuple被ack的时候,它会发一个消息给acker,告诉它这个tuple树发生了怎么样的变化。

      从上面这段话应该可以看出,每个tuple里面保存的id都是你说的spoutA发出的那个tuple的id;如你说的E发送失败,应该会调用fail方法的吧,那么传入的id值应该就是前面说的那个id,即spoutA发出的tuple的id,所以应该是从头重新发送了吧。

      我个人的理解,不知道到底是不是这么回事,也是刚看storm一周多,没有做过什么测试,所以都是猜想,希望可以多多交流。

    • 泣尘  说:

      看下面这段话:

      理解storm的可靠性的最好的方法是来看看tuple和tuple树的生命周期, 当一个tuple被创建, 不管是spout还是bolt创建的, 它会被赋予一个64位的id,而acker就是利用这个id去跟踪所有的tuple的。每个tuple知道它的祖宗的id(从spout发出来的那个tuple的id), 每当你新发射一个tuple, 它的祖宗id都会传给这个新的tuple。所以当一个tuple被ack的时候,它会发一个消息给acker,告诉它这个tuple树发生了怎么样的变化。

      从上面这段话应该可以看出,每个tuple里面保存的id都是你说的spoutA发出的那个tuple的id;如你说的E发送失败,应该会调用fail方法的吧,那么传入的id值应该就是前面说的那个id,即spoutA发出的tuple的id,所以应该是从头重新发送了吧。

      我个人的理解,不知道到底是不是这么回事,也是刚看storm一周多,没有做过什么测试,所以都是猜想,希望可以多多交流。

    • leo  说:

      看下面这段话:

      理解storm的可靠性的最好的方法是来看看tuple和tuple树的生命周期, 当一个tuple被创建, 不管是spout还是bolt创建的, 它会被赋予一个64位的id,而acker就是利用这个id去跟踪所有的tuple的。每个tuple知道它的祖宗的id(从spout发出来的那个tuple的id), 每当你新发射一个tuple, 它的祖宗id都会传给这个新的tuple。所以当一个tuple被ack的时候,它会发一个消息给acker,告诉它这个tuple树发生了怎么样的变化。

      从上面这段话应该可以看出,每个tuple里面保存的id都是你说的spoutA发出的那个tuple的id;如你说的E发送失败,应该会调用fail方法的吧,那么传入的id值应该就是前面说的那个id,即spoutA发出的tuple的id,所以应该是从头重新发送了吧。

      我个人的理解,不知道到底是不是这么回事,也是刚看storm一周多,没有做过什么测试,所以都是猜想,希望可以多多交流。

    • leo  说:

      发出这么多条….

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值