Kafka delivery保证(kafka消息投递保证)

本文深入探讨了Kafka中消息投递语义的不同层面,包括消息从Producer到Broker的AtLeastOnce、AtMostOnce和ExactlyOnce模式,以及消息从Broker到Consumer的ExactlyOnce模式。文章详细解释了如何通过配置Producer和Consumer行为来实现不同的消息投递保证,并讨论了实现ExactlyOnce保证所需的外部协调。同时,文章指出,虽然Kafka本身提供了AtLeastOnce保证,实现ExactlyOnce通常需要与外部存储系统协作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有这么几种可能的delivery guarantee:

  • At most once 消息可能会丢,但绝不会重复传输
  • At least one 消息绝不会丢,但可能会重复传输
  • Exactly once 每条消息肯定会被传输一次且仅传输一次,很多时候这是用户所想要的。  

  当Producer向broker发送消息时,一旦这条消息被commit,因数replication的存在,它就不会丢。但是如果Producer发送数据给broker后,遇到网络问题而造成通信中断,那Producer就无法判断该条消息是否已经commit。虽然Kafka无法确定网络故障期间发生了什么,但是Producer可以生成一种类似于主键的东西,发生故障时幂等性的重试多次,这样就做到了Exactly once。目前这一Feature还并未实现,有希望在Kafka未来的版本中实现。(所以目前默认情况下一条消息从Producer到broker是确保了At least once,可通过设置Producer异步发送实现At most once)。


  接下来讨论的是消息从broker到Consumer的delivery guarantee语义。(仅针对Kafka consumer high level API)。Consumer在从broker读取消息后,可以选择commit,该操作会在Zookeeper中保存该Consumer在该Partition中读取的消息的offset。该Consumer下一次再读该Partition时会从下一条开始读取。如未commit,下一次读取的开始位置会跟上一次commit之后的开始位置相同。当然可以将Consumer设置为autocommit,即Consumer一旦读到数据立即自动commit。如果只讨论这一读取消息的过程,那Kafka是确保了Exactly once。但实际使用中应用程序并非在Consumer读取完数据就结束了,而是要进行进一步处理,而数据处理与commit的顺序在很大程度上决定了消息从broker和consumer的消息投递语义保证。
  • 读完消息先commit消费状态(保存offset)再处理消息。这种模式下,如果Consumer在commit后还没来得及处理消息就crash了,下次重新开始工作后就无法读到刚刚已提交而未处理的消息,这就对应于At most once
  • 读完消息先处理再commit消费状态(保存offset)。这种模式下,如果在处理完消息之后commit之前Consumer crash了,下次重新开始工作时还会处理刚刚未commit的消息,实际上该消息已经被处理过了。这就对应于At least once。在很多使用场景下,消息都有一个主键,所以消息的处理往往具有幂等性,即多次处理这一条消息跟只处理一次是等效的,那就可以认为是Exactly once。(个人感觉这种说法比较牵强,毕竟它不是Kafka本身提供的机制,主键本身也并不能完全保证操作的幂等性。而且实际上我们说delivery guarantee语义是讨论被处理多少次,而非处理结果怎样,因为处理方式多种多样,我们不应该把处理过程的特性——如是否幂等性,当成Kafka本身的Feature)
  • 如果一定要做到Exactly once,就需要协调offset和实际操作的输出。经典的做法是引入两阶段提交。如果能让offset和操作输入存在同一个地方,会更简洁和通用。这种方式可能更好,因为许多输出系统可能不支持两阶段提交。比如,Consumer拿到数据后可能把数据放到HDFS,如果把最新的offset和数据本身一起写到HDFS,那就可以保证数据的输出和offset的更新要么都完成,要么都不完成,间接实现Exactly once。(目前就high level API而言,offset是存于Zookeeper中的,无法存于HDFS,而low level API的offset是由自己去维护的,可以将之存于HDFS中)
  总之,Kafka默认保证At least once,并且允许通过设置Producer异步提交来实现At most once。而Exactly once要求与外部存储系统协作,幸运的是Kafka提供的offset可以非常直接非常容易得使用这种方式。

参考资料

http://kafka.apache.org/documentation.html#semantics

http://www.jasongj.com/2015/03/10/KafkaColumn1/

### Kafka 中精确一次消息传递语义的实现 Apache Kafka 的 0.11 版本引入了精确一次 (exactly-once) 消息传递语义的支持,这是分布式流处理领域的一个重要里程碑[^1]。为了支持这一特性,Kafka 实现了两个核心功能:幂等性和事务。 #### 幂等性 (Idempotence) 幂等性确保即使生产者多次发送相同的消息到同一个分区,最终只会被记录一次。这通过为每个生产者分配唯一的生产者 ID (PID),并维护每条消息的序列号来实现。如果检测到重复的消息,则会丢弃该消息而不写入日志[^4]。 #### 事务支持 (Transactions) 除了幂等性外,Kafka 还提供了跨多个分区和消费者组的原子提交能力。这意味着可以将一组操作作为一个整体成功或失败,从而保证数据一致性。具体来说: - 生产者可以通过开启事务模式,在一个事务中执行多步操作。 - 当所有步骤完成时,生产者可以选择提交整个事务;如果有任何一步失败,则回滚整个事务[^2]。 这些机制共同作用下实现了端到端恰好一次交付语义[end-to-end exactly-once delivery semantics],即从输入源读取数据、经过计算再到输出目标存储过程中都能保持一致性的保障[^5]。 以下是利用 Kafka 提供的新特性的简单代码示例展示如何启用上述提到的功能之一——使用事务API来进行精准一次性投递: ```java Properties props = new Properties(); props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); props.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, "my-transactional-id"); KafkaProducer<String, String> producer = new KafkaProducer<>(props); producer.initTransactions(); try { producer.beginTransaction(); for(int i=0;i<10;i++) { producer.send(new ProducerRecord<>("topic", Integer.toString(i))); } producer.commitTransaction(); // Commit only if all sends succeed. } catch (ProducerFencedException | OutOfOrderSequenceException e){ producer.abortTransaction(); } finally{ producer.close(); } ``` 此段程序片段展示了怎样初始化交易环境并通过循环向指定主题发布一系列记录之后再决定是确认还是取消当前正在进行中的交易过程.
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值