实时计算的消息不丢失机制

目录

 

Storm消息不丢失       

ack 机制

如何使用Ack机制

如何关闭Ack机制

Ack机制基本实现

kafka消息不丢失

生产者(producer)

消费者(consumer)

服务器(broker)


Storm消息不丢失       

  • ack 机制

        Ack机制是storm整个技术体系中非常闪亮的一个创新点。通过Ack机制,spout发送出去的每一条消息,都可以确定是被成功处理或失败处理, 从而可以让开发者采取动作。比如在Meta中,成功被处理,即可更新偏移量,当失败时,重复发送数据。因此,通过Ack机制,很容易做到保证所有数据均被处理,一条都不漏。

ack机制即, spout发送的每一条消息:

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

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

        通过conf.put(Config.TOPOLOGY_MAX_SPOUT_PENDING, pending);设置spout pend数。(这个timeout时间可以通过Config.TOPOLOGY_MESSAGE_TIMEOUT_SECS来设定。Timeout的默认时长为30秒)

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

 

  • 如何使用Ack机制

* Storm的ack机制,默认是不开启的。需要程序员自己开启。

 

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

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

1)在nextTuple()方法中发送数据collector.emit方法的时候,需要指定messageid。这个messageid需要保证唯一。 可以使用UUID。SpoutOutputCollector类的方法如下

public List<Integer> emit(List<Object> tuple, Object messageId)

2)重写ack方法和fail方法。

    /**
     * 当spout发送的一条消息,被完整的处理了,storm框架会调用这个方法。
     * @param msgId 消息的唯一标号,是用户给定的。
     */
    public void ack(Object msgId) {
        System.out.println("--------------------------消息被处理成功啦!"+msgId);
    }

    /**
     * 当spout发送的一条消息,被标记为失败,storm框架会调用这个方法。
     * 如果处理失败,意味着开发人员要做一些动作,比如重新发送数据。
     * @param msgId 消息的唯一标号,是用户给定的。
     */
    public void fail(Object msgId) {
        System.out.println("--------------------------消息被处理失败啦!,需要重发"+msgId);
        String message = messageBuffer.get(msgId);
        collector.emit(new Values(message), msgId);
    }

3.Bolt中.

1)在execute方法中,处理完所有的业务逻辑之后。需要手动的ack以下  outputCollector.ack(input);

2)bolt如果产生了新的数据,需要锚点(Anchor)。让新的tuple和老的tuple产生关联。outputCollector.emit(oldTuple,new Values(word)),OutputCollector的方法如下:

public List<Integer> emit(Tuple anchor, List<Object> tuple) 

3) 当失败处理时,执行OutputCollector.fail(tuple); 不写的话等待响应时间后(30秒)会认为失败

 

BaseRichBolt需手动写以上两个方法,因为其不会主动ack或fail;

OutputCollector.fail(tuple)就是主动告知失败

OutputCollector.ack(tuple)主动告知成功

所以推荐使用IbasicBolt/BaseBasicBolt, 因为IBasicBolt 自动封装了OutputCollector.ack(tuple), 处理失败时,请抛出FailedException,则自动执行OutputCollector.fail(tuple)

  • 如何关闭Ack机制

 

有2种途径:

spout发送数据时不带上msgid

Topology设置acker数等于0

 

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

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

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

流程如下:

1. Spout在初始化时会产生一个tasksId;

2. Spout中创建新的Tuple,其id是一个64位的随机数;

3. Spout将新建的Tuple发送出去(给出了messageId来开启Tuple的追踪), 同时会发送一个消息到某个acker,要求acker进行追踪。该消息包含两部分:

  • Spout的taskId:用户acker在整个tuple树被完全处理后找到原始的Spout进行回调ack或fail
  • 一个64位的ack val值: 标志该tuple是否被完全处理。初始值为0。

3. 一个Bolt在处理完Tuple后,如果发射了一个新的anchor tuple,Storm会维护anchor tuple的列表;

4. 该Bolt调用OutputCollector.ack()时,Storm会做如下操作:

  • 将anchor tuple列表中每个已经ack过的和新创建的Tuple的id做异或(XOR)。假定Spout发出的TupleID是tuple-id-0,该Bolt新生成的TupleID为tuple-id-1,那么,tuple-id-0 XOR tuple-id-1 (如果左右一样则等于0)
  • Storm根据该原始TupleID进行一致性hash算法,找到最开始Spout发送的那个acker,然后把上面异或后得出的ack val值发送给acker

5. acker收到新的ack val值后,与保存的原始的Tuple的id进行异或,如果为0,表示该Tuple已被完全处理,则根据其taskId找到原始的Spout,回调其ack()方法。

kafka消息不丢失

  • 生产者(producer)

  1. 消息生产分为同步模式和异步模式
  2. 消息确认分为三个状态
    1. 生产者只负责发送数据
    2. 某个partition的leader收到数据给出响应
    3. 某个partition的所有副本都收到数据后给出响应
  3. 在同步模式下
    1. 生产者等待10S,如果broker没有给出ack响应,就认为失败。
    2. 生产者重试3次,如果还没有响应,就报错。
  4. 在异步模式下
    1. 先将数据保存在生产者端的buffer中。Buffer大小是2万条。
    2. 满足数据阈值或者数量阈值其中的一个条件就可以发送数据。
    3. 发送一批数据的大小是500条。

如果broker迟迟不给ack,而buffer又满了。开发者可以设置是否直接清空buffer中的数据。

  • 消费者(consumer)

 1.消息存储机制

在 Kafka 文件存储中,同一个 topic 下有多个不同 partition,每个 partition 为一个目录,每个 partion(目录)相当于一个巨型文件被平均分配到多个大小相等 segment(段)数据文件中。

         segment中有两个核心的文件一个是log (存放消息本身),一个是index (存放offset)。 当log文件等于1G时,新的消息会写入到下一个segment中。

一个segment段差不多会存储70万条数据。(路径在/config/server.properties配置文件的 log.dirs=/export/data/kafka)

2. 消费记录存储机制

另外有个外部存储能够记录每个Consumer消费partition的offset值.

          只要外部存储记录的offset值还在,消费者端不会存在消息不丢失的可能。只会重复消费。

在0.8.2之前,comsumer定期提交已经消费的kafka消息的offset位置到zookeeper中保存。对zookeeper而言,每次写操作代价是很昂贵的,而且zookeeper集群是不能扩展写能力的。在0.8.2开始,可以把comsumer提交的offset记录在compacted topic(__comsumer_offsets)中,该topic设置最高级别的持久化保证,即ack=-1。__consumer_offsets由一个三元组< comsumer group, topic, partiotion> 组成的key和offset值组成,在内存也维持一个最新的视图view,所以读取很快。
  kafka可以频繁的对offset做检查点checkpoint,即使每消费一条消息提交一次offset。
  在0.8.1中,已经实验性的加入这个功能,0.8.2中可以广泛使用。

  • 服务器(broker)

broker端的消息不丢失,其实就是用partition副本机制来保证。

Producer  ack  -1. 能够保证所有的副本都同步好了数据。其中一台机器挂了,并不影像数据的完整性。

 

参考: https://www.cnblogs.com/intsmaze/p/6709297.html

 

猜你喜欢

转载自blog.csdn.net/b_oyidyt/article/details/82314159
今日推荐