Kafka幂等性原理简介

文章目录

    • kafka幂等性
    • 幂等性开启
    • 幂等性实现原理
      • PID
      • sequence numbers
      • client端幂等性发送流程
      • server端幂等性接收流程
      • 幂等性对有序性的影响

kafka幂等性

Kafka Producer 的幂等性指的是当发送同一条消息时,数据在 Server 端只会被持久化一次,数据不丟不重,但是这里的幂等性是有条件的:

  • 只能保证 Producer 在单个会话内不丟不重,如果 Producer 出现意外挂掉再重启是无法保证的(幂等性情况下,是无法获取之前的状态信息,因此是无法做到跨会话级别的不丢不重);
  • 幂等性不能跨多个 Topic-Partition,只能保证单个 partition 内的幂等性,当涉及多个 Topic-Partition 时,这中间的状态并没有同步。

如果需要跨会话、跨多个 topic-partition 的情况,需要使用 Kafka 的事务性来实现。

幂等性开启

将 Producer 的配置 enable.idempotence 设置为 true 即可:
props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, “true”);
而且当 enable.idempotence 为 true,Producer的ack属性默认为 all。

幂等性实现原理

PID

每个 Producer 在初始化时都会被分配一个唯一的 PID。 PID 是全局唯一的,Producer 故障后重新启动后会被分配一个新的 PID,这也是幂等性无法做到跨会话的一个原因。

Producer的PID需要向sever端进行申请。而server端向 ZooKeeper 申请。zk 中有一个 /latest_producer_id_block 节点,每个 Broker 向 zk 申请一个 PID 段后,都会把自己申请的 PID 段信息写入到这个节点,这样当其他 Broker 再申请 PID 段时,会首先读写这个节点的信息,然后根据 block_end 选择一个 PID 段,最后再把信息写会到 zk 的这个节点。broker向producer返回自己保存的PID,如果PID用尽时,向zookeeper申请新的PID段。

sequence numbers

对于一个给定的 PID,sequence number 将会从0开始自增,每个 Topic-Partition 都会有一个独立的 sequence number。有了 PID 之后,在 PID + Topic-Partition 级别上添加一个 sequence numbers 信息,就可以实现 Producer 的幂等性了。Producer端会保存每一个Topic-Partition的 last sequence number,在发送消息之前,会在消息的meta中添加此条消息的 sequence number。

client端幂等性发送流程

  1. producer send方法将消息加入缓存区。添加时会判断是否需要新建一个 ProducerBatch,这时这个 ProducerBatch 还是没有 PID 和 sequence number 信息的;
  2. sender发送线程,在run方法中,判断当前的 PID 是否需要重置。重置的原因是因为:如果有 topic-partition 的 batch 重试多次失败最后因为超时而被移除,这时 sequence number 将无法做到连续,因为 sequence number 有部分已经分配出去,这时系统依赖自身的机制无法继续进行下去(因为幂等性是要保证不丢不重的),相当于程序遇到了一个 fatal 异常,PID 会进行重置。遇到这个问题时是无法保证 exactly once 的(有数据已经发送失败了,并且超过了重试次数);
  3. 判断是否需要申请 PID,如果需要的话,这里会阻塞直到获取到相应的 PID 信息;
  4. 判断这个 topic-partition 是否可以继续发送(如果出现前面2中的情况是不允许发送的)、判断 PID 是否有效、如果这个 batch 是重试的 batch,那么需要判断这个 batch 之前是否还有 batch 没有发送完成,如果有,这里会先跳过这个 Topic-Partition 的发送,直到前面的 batch 发送完成
  5. 如果这个 ProducerBatch 还没有这个相应的 PID 和 sequence number 信息,会在这里进行相应的设置
  6. 发送 ProduceRequest 请求

server端幂等性接收流程

  1. 检查是否有 PID 信息,没有的话走正常的写入流程
  2. 先根据 batch 的 sequence number 信息检查这个 batch 是否重复(server 端会缓存 PID 对应这个 Topic-Partition 的最近5个 batch 信息)。如果有重复,这里当做写入成功返回。
  3. 有了 PID 信息,并且不是重复 batch 时。检查该 PID 是否已经缓存中存在。
  4. 如果不存在,那么判断 sequence number 是否 从0 开始,是的话,在缓存中记录 PID 的 meta,并执行写入操作,否则返回 UnknownProducerIdException。
  5. 如果该 PID 在缓存中存在,先检查 PID epoch 与 server 端记录的是否相同;如果不同并且 sequence number 不从 0 开始,那么返回 OutOfOrderSequenceException 异常;如果不同并且 sequence number 从 0 开始,那么正常写入;如果相同,那么根据缓存中记录的最近一次 sequence number(currentLastSeq)检查是否为连续,不连续的情况下返回 OutOfOrderSequenceException 异常。

幂等性对有序性的影响

Prodcuer开启幂等性会保证消息在broker中的Topic-partition的有序性,因为幂等性是基于sequence number的。如果没有开启幂等性,由于失败重试,可能会导致消息的乱序。

参考博客:
http://matt33.com/2018/10/24/kafka-idempotent/

你可能感兴趣的:(Kafka)