网址: 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的产生(一个个单词), 这个消息流大概是这样的:
在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树所发生的变化。
关于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树里面, 也就不会被跟踪了。
Pingback 引用通告: Twitter Storm: 在生产集群上运行topology | 徐明明
Pingback 引用通告: Twitter Storm: storm的一些常见模式 | 徐明明
storm为了保证可靠性处理是否必须要存储还没有完全处理的Turple?这样发送Turple的Spout是否会出现OOM?
文章中已经提到了:
所以我们要做的是快速告诉storm我们对消息的处理结果(ack或者fail).
也就是说,在execute的方法中,如果方法体正常执行,就立马调用ack方法。执行时发生异常,即调用fail方法。如此可以避免由Storm框架自行检测浪费更多的时间和性能。是这样的吗?
是否有相关的性能报告或者benchmark,storm的性能达到多少?
Pingback 引用通告: Twitter Storm源代码分析之acker工作流程 | 徐明明
Pingback 引用通告: Twitter Storm入门 | 徐明明
对于一些没有ack或fail的queue,例如redis的pub sub,spout的fail被调用的时候,只拿到一个id,却拿不到msg的内容。有办法拿回dequeue出来原始的msg吗?或者tuple。
不行, 你的代码要能够根据storm的tuple-id, 重新发射这个tuple。比如把没ack的tuple放在一个list里面,处理完了,从list里面清除。
1. 由于对应的task挂掉了,一个tuple没有被ack: storm的超时机制在超时之后会把这个tuple标记为失败,从而可以重新处理。
2. Acker挂掉了: 这种情况下由这个acker所跟踪的所有spout tuple都会超时,也就会被重新处理。
—————————————————————————-
楼主,以上两点这样理解我觉得才对:
1、在tuple超时时候,发射tuple的那个spout回被回调fail方法。
2、你需要自己来编写逻辑来重新处理fail掉的tuple,比如放入一个列表,在nextTuple()中获取这些失败的tuple在发射出去。而不是说storm会自动帮你重新发射tuple。
对吗?
是的,重发不是自动的。
但是它怎么知道哪些tuple fail掉了?
一种是bolt处理tuple的过程中发生了错误 显式的fail掉的,一种是超时时间到还没有被ack的,这些fail的tuple都会回调到spout的fail接口让你处理。
Pingback 引用通告: Twitter Storm: Transactional Topolgoy简介 | 徐明明
“每个你处理的tuple, 必须被ack或者fail。因为storm追踪每个tuple要占用内存。所以如果你不ack/fail每一个tuple, 那么最终你会看到OutOfMemory错误。”
既然有这种问题, 为什么storm在最初设计的时候, 不采用每个bolt执行完之后内部主动去调用collector.ack, 应用方只需要根据逻辑来决定是否需要调用fail即可.
自动ack之后还怎么fail呢? ack表示这个tuple已经“成功了“, 内存里面就不再对了进行追踪了, 你后面还怎么”fail”呢?应该是不行的。
“每个你处理的tuple, 必须被ack或者fail。因为storm追踪每个tuple要占用内存。所以如果你不ack/fail每一个tuple, 那么最终你会看到OutOfMemory错误。”
既然有这种问题, 为什么storm在最初设计的时候, 不采用每个bolt执行完之后内部主动去调用collector.ack, 应用方只需要根据逻辑来决定是否需要调用fail即可.
不知道ack或者fail之后,storm会进行哪些操作,源码里没有看到相关GC的动作,怎么就能防止OOM了呢?
GC的动作?你是指主动触发GC? ack之后storm是会从一个map里面把ack过的对象干掉的,但是理论上如果你发的ack很多,确实存在OOM的可能。你可以通过设置max_spout_pending来调整系统内未ack/fail的 tuple数量。
如果设置了 max_spout_pending, storm的机制是减少spout的发射量,还是丢弃超过max_spout_pendin的tuple的跟踪信息?
如果设置了 max_spout_pending, storm的机制是减少spout的发射量,还是丢弃超过max_spout_pendin的tuple的跟踪信息?
“一个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的结果?
xumingming 您好!
请教一下,我们同一个spout源数据有多个业务处理场景,比如一个bolt直接进行存储数据到DB,另几个bolt进行不同的统计计算,当一个bolt宕掉后,其它bolt因为已经将数据存储到DB了,所以不想重复处理数据,又不想和数据库进行频繁交互有什么好的方式实现么?数据库我们使用mongoDB,当数据量大到一定程度后insert+update效率非常差
楼主,我自己写的storm job在Storm UI上报了很多fail而且fail的Tuple远远大于正常处理的Tuple。从未遇到过fail的情况,找不到原因。Bolt我用的IBasicBolt,请问能给我举一个发生fail的Topology的例子么?不胜感激。
1、由于对应的task挂掉了,一个tuple没有被ack: storm的超时机制在超时之后会把这个tuple标记为失败,从而可以重新处理。
这个对于那种一个bolt, fieldsGrouping 接受tuple的情况,比如wordcount那个bolt,假如bolt用2个任务来执行(暂定叫a任务,和b任务吧),一个单词比如 cat,总是会流到a任务中执行,然后记录cat的数量,但是如果a任务死了,正在流的一个“cat” 就要重新发送,这时候会发送b任务吗? 如果发送到了b任务,哪cat的数量计数不就错误了吗?
同问
Pingback 引用通告: storm 原理简介及单机版安装指南(转) | 盛大创新院团队博客 | 数据,更懂人心
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的操作岂不是要做两遍,出现重复处理?
问个问题:acker这个task是运行在控制节点上呢,还是运行在工作节点上?如何判断一个task运行在哪类节点上?
是运行在supervisor也就是工作节点上的。有个storm ui可以看到。
你好,有个疑问:bolt在anchoring接受到的tuple时,是不是会向相应的acker发送一个消息也就是文中说的“首先,在你生成一个新的tuple的时候要通知storm。”?
你好,有个疑问:bolt在anchoring接受到的tuple时,是不是会向相应的acker发送一个消息也就是文中说的“首先,在你生成一个新的tuple的时候要通知storm。”?
“它只是简单地把这棵树上的所有创建的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了, 它知道这棵树已经处理完成了。
还应该再加一句:“当tuple树中只有根节点存在时,计算ack val的时候,根tuple的tupleid和0进行XOR计算。”
有问题请教一下。
在tuple层面去掉可靠性,是说在某些bolt 发射tuple的时候,不将新生成的tuple与其父tuple anchor在一起,这样,新生成的tuple就不在tuple树里。是这样吧?
这就是说,只有spout发射的tuple才能成为一棵tuple树的根节点,而blot发射的tuple,如果被unanchor了,就将被acker忽略,而不能以其为根再建一颗新的tuple树。我这样理解对吗?
应该是对的。
另外,我做了一些测试,感觉应该不是用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
Pingback 引用通告: Storm基础
你好,我初看storm一周多点时间,我想问下,我有一点不明白,假如我从spout A发出一个tuple到bolt B,该bolt把该tuple转换成多个tuples,分发给多个bolts C,D,E,我的问题是如果E输出tuple失败,那么spout A会重新发送一开始的整个未被拆分的tuple,重新发送运行吗?还是通知失败bolt 的父bolt重新发送相应的tuple呐???
你好,我初看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一周多,没有做过什么测试,所以都是猜想,希望可以多多交流。
看下面这段话:
理解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一周多,没有做过什么测试,所以都是猜想,希望可以多多交流。
发出这么多条….