Storm 消息容错机制和通信四

ack 是什么

ack 机制是 storm 整个技术体系中非常闪亮的一个创新点。

通过 ack 机制,spout 发送的每一条信息,都可以确定是被成功处理或失败处理,从而可以让开发者采取行动。比如在meta中,成功被处理,即可更新偏移量,当失败时,重复发送处理。

因此,通过 ack 机制,很容易做到保证所有数据均被处理,一条不漏。

另外需要注意的, 当 spout 触发 fail 动作时,不会自动重发失败的 tuple ,需要 spout 自己重新获取数据,重发一次。

ack 机制

spout 发送每一条信息 

  1. 在规定的时间内,spout 收到 Acker 的 ack 响应,即任务该 tuple 被后续 Bolt 成功 处理。
  2. 在规定的时间内,spout 未收到 Acker 的 ack 响应 tuple ,就出发 fail 动作,认为该 tuple 处理失败。
  3. 或者收到 Acker 发送的 fail 响应 tuple,也任务失败,触发 fail 动作。

另外Ack机制还常用于限流作用: 为了避免spout发送数据太快,而bolt处理太慢,常常设置pending数,当spout有等于或超过pending数的tuple没有收到ack或fail响应时,跳过执行nextTuple, 从而限制spout发送数据。

通过conf.put(Config.TOPOLOGY_MAX_SPOUT_PENDING, pending);设置spout pend数。

如何使用 Ack 机制

spout 在发送数据的时候带上 msgid

设置 acker 数至少大于 0; Config.setNumAckers(conf, ackerParal);

在 bolt 中完成处理 tuple 时,执行 OutputCollector.ack( tuple ),处理失败时,请抛出 FailedException,则自动执行 OutputCollector.fail( tuple );

如何关闭 Ack 机制

有两种方式

  1. spout 发送数据时不带上 msgid
  2. 设置 acker 数等于 0 

 

基本实现

Storm 系统中有一组叫做 acker 的特殊任务,他们负责跟踪 DAG (有向无环图) 中的每个消息。

acker 任务保存了 spout id 的一堆值的映射。第一个值就是 spout 的任务 id ,通过这个id ,acker 就知道消息处理完时该通知哪个 spout 任务 。第二个值是一个 64bit 的数字,我们称之为 ack val ,它是 树中所有消息的随机 id 的异或计算结果。

<TaskId,<RootId,ackValue>>

Spoutid,<系统生成的id,ackValue>

Task-0,64bit,0

ack val 表示了整棵树的状态,无论这棵树多大,只需要这个固定大小的数字就可以跟踪整棵树。当消息被创建和被应答的时候都会有相同的消息 id 发送过来做异或。每当acker 发现一棵树的 ack val 值为0 的时候,他就知道这棵树已经被完全处理了。

 

 


Storm 通信机制

Worker之间的通信经常需要通过网络跨节点进行,Storm使用 ZeroMQ 或 Netty 作为进程通信的消息框架。

Worker进程内部通信:不同的 woker 的 thread 通信通常使用 LMAX Disruptor 来完成。

Worker 进程间通信

worker 进程间消息传递机制,消息的接受和处理大概流程如下图:

对于 worker 进程来说,为了管理流入 和 流出 的消息,每个 worker 进程有一个独立的接收线程(对配置的TCP端口 supervisor.slots.ports 进行监听 );

对应 Worker 接受线程,每个 worker 存在一个独立的发送线程,它负责从 worker 的的 transfer-queue 中读取消息,并通过网络发送给其他worker; 

每个 executor 有自己的 incoming-queue 和 outgoing-queue。Worker 接受线程将收到的消息通过 task 编号传递给对应的 executor(一个或多个)  incoming-queue;

每个 executor 有单独的线程分别处理 spout/bolt 的业务逻辑,业务逻辑输出的中间数据 会存放在 outgoing-queue 中,当 executor 的 outgoing-queue 中的 tuple 达到一定的阀值, executor 的发送线程将批量获取 outgoing-queue 中的 tuple ,并发送到 transfer-queue中。

每个 worker 进程控制一个或多个 executor 线程,用户可在代码中进行配置。其实就是我们在代码中设置的并发度个数。

Worker 进程中通信分析

http://images.cnitblog.com/blog/312753/201307/23153829-4ebd07b835ca46408f9e1b1f6282fe84.png

1:Worker 接受线程通过网络接受数据,并根据 Tuple 中包含的 taskId ,匹配到对应的 executor;然后根据 executor 找到对应的 incoming-queue,将数据发送到 incoming-queue 队列中。

2: 业务逻辑执行线程消费 incoming-queue 的数据,通过 blot 的 execute(***) 方法,将 Tuple 作为参数传输给用户自定义的方法。

3:业务逻辑执行完毕之后,将计算的中间数据发送给 outgoing-queue 队列,当outgoing-queue 中的 tuple 达到一定的阈值,executor 的发送线程将批量获取 outgoing-queue 中的 tuple,并发送到 Worker 中的 transfer-queue 中。

4:Worker 发送线程消费 transfer-queue 中的数据,计算 tuple 的目的地,连接不同的 node + port 将数据通过网络传输的方式送到另一个Worker.

5:另一个 worker 中进行以上步骤1 操作(开始重复)。

发布了33 篇原创文章 · 获赞 3 · 访问量 5862

猜你喜欢

转载自blog.csdn.net/WandaZw/article/details/83414239