kafka怎么保证不丢失消息和不重复消费消息

本文介绍Kafka如何确保消息的准确性和一致性,包括生产者消息推送、消费者消息消费的不同策略,以及如何通过ISR、ACK机制和语义级别保证消息不丢失且不重复。

给大家推荐一款好用的优快云云服务,新人首购折扣哦,点击下图跳转:
优快云开发云

kafka学习目录:kafka目录


Kafka中怎么保证消息不会丢失和不重复消费?

从两个方面分析:消费推送和消息消费。也就是生产者和消费者两方面。

首先我们需要知道topic的概念。

  • Topic : 话题,可以理解为一个队列, 生产者和消费者面向的都是一个 topic。
  • 一个topic又有多个partition,而每个分区都有若干个副本:一个 leader 和若干个 follower。

生产者在推送消息时,会确定topic和topic中的那个partition。

一个消费者组内每个消费者负责消费 一个topic中不同分区的数据,同一个分区同时只能由一个组内消费者消费

一、生产者推送消息时怎么保证消息不丢失和不重复

对于生产者,在推送消息的时候,有以下几种方式来确定topic、topic中的partition。

将 producer 发送的数据封装成一个 ProducerRecord 对象。

img

  1. 指明 partition 的情况下,直接将指明的值直接作为 partiton 值;
  2. 没有指明 partition 值但有 key 的情况下,将 key 的 hash 值与 topic 的 partition 数进行取余得到 partition 值;
  3. 既没有 partition 值又没有 key 值的情况下,第一次调用时随机生成一个整数(后面每次调用在这个整数上自增),将这个值与 topic 可用的 partition 总数取余得到 partition值,也就是常说的 round-robin 算法。

1.1、总体概况

为保证 producer 发送的数据,能可靠的发送到指定的 topic, topic 的每个 partition 收到producer 发送的数据后,并等待该分区中全部的follower同步完成,该分区的leader才向 producer 发送 ack(acknowledgement: 确认收到),如果producer 收到 ack, 就会进行下一轮的发送,否则重新发送数据。

而为了处理follower在同步数据时发生故障,导致leader一直等待下去的情况,新增了ISR的机制。

1.1.2、什么是ISR呢?

Leader 维护了一个动态的 in-sync replica set (ISR:同步副本),意为和 leader 保持同步的 follower 集合。当 ISR 中的 follower 完成数据的同步之后,leader 就会给 producer 发送 ack。如果 follower长时间未向leader同步数据,则该 follower 将被踢出 ISR,该时间阈值由replica.lag.time.max.ms参数设定。而如果Leader 发生故障,就会从 ISR 中选举出新的 leader。

1.1.3、ACK机制

对于某些不太重要的数据,对数据的可靠性要求不是很高,能够容忍数据的少量丢失,所以没必要等 ISR 中的 follower 全部接收成功,才返回ack。

所以 Kafka 为用户提供了三种可靠性级别,用户根据对可靠性和延迟的要求进行权衡,选择以下的配置。

acks 参数配置

  • 0: producer 不等待 broker(或者说是leader)的 ack,这一操作提供了一个最低的延迟, broker 一接收到还没有写入磁盘的数据就已经返回,当 broker 故障时有可能丢失数据
  • 1: producer 等待 broker 的 ack, partition 的 leader 落盘成功后返回 ack,如果在 follower同步成功之前 leader 故障,那么将会丢失数据
  • -1(all) : producer 等待 broker 的 ack, partition 的 leader 和 ISR 里的follower 全部落盘成功后才返回 ack。但是如果在 follower 同步完成后, broker 发送 ack 之前, leader 发生故障,那么会造成数据重复。(假如ISR中没有follower,就变成了 ack=1 的情况)
1.1.4、三种语义

At Most Once 语义:

  • 将服务器 ACK 级别设置为 0,可以保证生产者每条消息只会被发送一次,即 At Most Once 语义。
  • 此语义可以保证数据不重复,但是不能保证数据不丢失。

At Least Once 语义:

  • 将服务器的 ACK 级别设置为-1(all),可以保证 Producer 到 Server 之间不会丢失数据,即 At Least Once 语义。
  • 此语义可以保证数据不丢失,但是不能保证数据不重复。

Exactly Once 语义:

  • At Least Once + 幂等性 = Exactly Once
  • 幂等性:所谓的幂等性就是指 Producer 不论向 Server 发送多少次重复数据, Server 端都只会持久化一条。
  • 要启用幂等性,只需要将 Producer 的参数中 enable.idempotence 设置为 true 即可(此时 ack= -1)。 Kafka的幂等性实现其实就是将原来下游需要做的去重放在了数据上游。原理:开启幂等性的 Producer 在初始化的时候会被分配一个 PID,发往同一 Partition 的消息会附带 Sequence Number。而Broker 端会对<PID, Partition, SeqNumber>做缓存,当具有相同主键的消息提交时, Broker 只会持久化一条。
  • 但是 PID 重启就会变化,同时不同的 Partition 也具有不同主键,所以幂等性无法保证跨分区、跨会话的 Exactly Once。(也就是说它只解决单次会话、单个分区里的消息重复问题)

1.2、过程总结

总结以上,可以得知生产者在推送消息时,依靠的是ISR、ACK机制、以及三种语义来达到不同情况的消息准确性。

所以总的过程应该是这样的: producer 向指定的 topic和partition发送数据, topic 的每个 partition 收到producer 发送的数据后,(下一步是等待ISR的follower同步完成,这一步会根据ack的参数配置[0,1,-1],确定具体的ack返回时机),该分区的leader向 producer 发送 ack(acknowledgement: 确认收到),如果producer 收到 ack, 就会进行下一轮的发送,否则重新发送数据。

而如果要保证生产者推送到服务器里的消息数据即不重复又不丢失,就要使用Exactly Once语义:将ack参数配置为-1,并开启幂等性(enable.idempotence= true)。

1.3、follower与leader出故障,怎么保证数据的一致性

follower 和 leader 发生故障了,该怎么处理。

img
  • LEO:(Log End Offset)每个副本的最后一个offset;
  • HW:(High Watermark)高水位,指的是消费者能见到的最大的 offset, ISR 队列中最小的 LEO;

follower 故障和 leader 故障:

  • follower 故障:follower 发生故障后会被临时踢出 ISR,待该 follower 恢复后, follower 会读取本地磁盘记录的上次的 HW,并将 log 文件高于 HW 的部分截取掉,从 HW 开始向 leader 进行同步。等该 follower 的 LEO 大于等于该 Partition 的 HW,即 follower 追上 leader 之后,就可以重新加入 ISR 了。
  • leader 故障:leader 发生故障之后,会从 ISR 中选出一个新的 leader,之后,为保证多个副本之间的数据一致性, 其余的 follower 会先将各自的 log 文件高于 HW 的部分截掉,然后从新的 leader同步数据。

注意: 这只能保证副本之间的数据一致性,并不能保证数据不丢失或者不重复。ack是负责数据丢失的

二、消费者丢失消息和重复消费消息的情况

consumer 采用 pull(拉) 模式从 broker 中读取数据。这个过程只涉及到了服务器和消费者两方,那消费者是怎么保证不丢失和不重复的获取消息呢?

关键在于consumer会维护一个offset,该offset实时记录着自己消费的位置。同时消费者能见到的最大的 offset,是HW, 是ISR 队列中最小的 LEO【这一点看1.3】,所以只要保证offset不出错,那消息就不会丢失或者重复消费。但是offset的维护并不是那么简单,它分为好几种方式。

offset的维护方式:详细代码可参考:Kafka API(代码编写)

  • 自动提交
    • enable.auto.commit:是否开启自动提交 offset 功能,消费者只在启动的时候去访问offset的值,如果将该值配置为false,就要手动提交offset,否则offset就不会更新。
    • auto.commit.interval.ms:自动提交 offset 的时间间隔
  • 手动提交
    • commitSync(同步提交)
    • commitAsync(异步提交)
    • 两者的相同点是:都会将本次 poll 的一批数据最高的偏移量提交;
    • 不同点是:commitSync 阻塞当前线程,一直到提交成功,并且会自动失败重试(由不可控因素导致,也会出现提交失败);而 commitAsync 则没有失败重试机制,故有可能提交失败。
    • 无论是同步提交还是异步提交 offset,都有可能会造成数据的漏消费或者重复消费。先提交 offset 后消费,有可能造成数据的漏消费;而先消费后提交 offset,有可能会造成数据的重复消费。
  • 自定义存储offset
    • offset 的维护是相当繁琐的, 因为需要考虑到很多东西,例如消费者的 Rebalace。

三、总结

在业务的运用中。

对于消息重复,这个影响不是很严重,无论是生产者重复推送数据,还是消费者重复拉取数据,只要在消费端落库时,手动做去重就可以了。

对于消息丢失:

  • consumer端丢失消息的情形比较简单:如果在消息处理完成前就提交了offset,那么就有可能造成数据的丢失。由于Kafka consumer默认是自动提交位移的,所以在后台提交位移前一定要保证消息被正常处理了,因此不建议采用很重的处理逻辑,如果处理耗时很长,则建议把逻辑放到另一个线程中去做。为了避免数据丢失,可以采用手动提交offset:(1)enable.auto.commit=false 关闭自动提交位移、(2)在消息被完整处理之后再手动提交位移
  • 生产者丢失消息是最复杂的情形了。生产者(Producer) 使用 send 方法发送消息实际上是异步的操作,我们可以通过 get()方法获取调用结果,但是这样也让它变为了同步操作,但是一般不推荐这么做!可以采用为其添加回调函数的形式。这个回调函数会在 Producer 收到 ack 时调用,此处就和acks参数配置[1、0、-1]密切相关了,详细代码可参考:Kafka API(代码编写)
    • 如果消息发送失败的话,我们检查失败的原因之后重新发送即可!另外这里推荐为 Producer 的retries(重试次数)设置一个比较合理的值,一般是 3 ,但是为了保证消息不丢失的话一般会设置比较大一点。设置完成之后,当出现网络问题之后能够自动重试消息发送,避免消息丢失。另外,建议还要设置重试间隔,因为间隔太小的话重试的效果就不明显了,网络波动一次,你3次一下子就重试完了。
<think>首先,用户的问题是关于Kafka如何保证消息不丢失重复消费。我需要基于系统级指令来构建我的回答。 系统级指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 - 回答结构清晰,帮助用户逐步解决问题,保证真实可靠。 用户提供了引用内容,我需要参考这些引用: - 引用[1]: 关于Kafka保证重复消费不丢失数据的问题。 - 引用[2]: Kafka Consumer机制优化,保证至少消费一次。 - 引用[3]: Kafka避免消息重复消费的机制。 - 引用[4]: 在consumer消费阶段处理offset。 - 引用[5]: 关于消息丢失重复消费的细节。 用户的问题:"请问Kafka如何保证消息不丢失重复消费" 我需要确保回答覆盖两个部分:如何保证消息不丢失,以及如何保证消息重复消费。 结构化我的回答: 1. **介绍**:简要解释Kafka消息传递中的重要性。 2. **保证消息不丢失**: - 生产者端:acks设置、重试机制。 - Broker端:副本机制、ISR。 - 消费者端:手动提交offset。 3. **保证消息重复消费**: - 消费者配置:关闭自动提交,手动提交offset。 - 消费者组协调。 4. **总结**:强调最佳实践。 5. **生成相关问题**:基于主题生成相关问题。 在回答中,自然地添加引用标识。例如,如果我从引用[5]中提取信息,就在段落末尾添加[^5]。 确保使用正确的LaTeX格式: - 行内数学表达式:$...$,例如变量。 - 独立公式:如果需要,单独成段,但在这个主题中,可能需要太多数学公式。Kafka涉及offset、acks等,可能需要数学表达式,除非有数值示例。但指令说所有行内数学表达式必须使用$...$,所以如果有变量或简单表达式,就用$...$包裹。例如,offset可以写成$offset$,但可能需要,因为它是数学表达式。指令说"包括变量、等式、方程等",所以offset可以视为变量,写成$offset$。 在引用中,有像acks这样的参数,所以我可以写成$acks$。 生成相关问题:在回答结束时,添加"§§相关问题§§"部分,列出3-5个相关问题。 现在,构建回答大纲: - **开头**:直接回答问题,概述Kafka的机制。 - **保证消息不丢失**: - 生产者端:acks配置(如$acks=all$)、重试机制。 - Broker端:副本(replication)、ISR(In-Sync Replicas)。 - 消费者端:手动提交offset,避免过早提交。 - **保证消息重复消费**: - 关闭自动提交offset(enable.auto.commit=false)。 - 手动提交offset在消息处理完成后。 - 消费者组管理。 - **总结**:强调结合配置实践。 - **相关问题**:生成相关问题。 添加引用: - 当提到生产者端时,参考引用[5]。 - 当提到消费者端offset提交时,参考引用[2]、[3]、[4]、[5]。 确保回答真实可靠:基于Kafka的官方文档常见实践。 最终回答用中文。</think>### Kafka如何保证消息不丢失重复消费 Kafka作为一款高性能的分布式消息队列系统,广泛应用于日志收集、实时流处理等场景。消息传递的可靠性是其核心特性之一,包括避免消息丢失(确保数据不丢失避免消息重复消费(确保重复处理)。下面我将逐步解释Kafka如何实现这两个目标,结合其内部机制最佳实践。回答基于Kafka的官方文档常见实践,确保真实可靠。 #### 1. **保证消息不丢失** 消息丢失可能发生在生产者、Broker或消费者端。Kafka通过多级机制来最小化风险: - **生产者端**: - 生产者(Producer)发送消息时,通过配置$acks$参数来确保消息被Broker持久化。$acks$有三个可选值: - $acks=0$:生产者等待Broker确认,风险最高,可能导致消息丢失。 - $acks=1$:生产者等待Leader Broker确认写入,但如果Leader故障且副本未同步,消息可能丢失。 - $acks=all$(或$acks=-1$):生产者等待所有ISR(In-Sync Replicas)副本确认写入,这是最安全的模式,能保证消息不丢失(除非所有副本都故障)。 同时,生产者支持重试机制:设置$retries$参数(例如$retries=3$)重试间隔,当网络故障或Broker可用时自动重发消息。这能有效避免因临时问题导致的数据丢失[^5]。 - **Broker端**: - Kafka使用副本机制(Replication)来保障高可用。每个分区(Partition)有多个副本,其中Leader负责读写,Followers同步数据。通过配置$min.insync.replicas$(例如设为2),确保至少指定数量的副本在ISR列表中。如果Leader故障,Kafka会自动从ISR中选举新Leader,避免数据丢失。 - Broker还通过持久化日志(写入磁盘)定期刷盘(flush)来保证消息因宕机而丢失[^1]。 - **消费者端**: - 消费者(Consumer)在消费消息时,必须避免过早提交偏移量(offset)。如果消费者在消息处理前就提交offset,但处理失败,消息可能丢失。 - 最佳实践:关闭自动提交offset(设置$enable.auto.commit=false$),并在消息处理完成后手动提交offset(例如使用$consumer.commitSync()$)。这确保只有成功处理的消息才被标记为已消费[^5][^4]。 总结:保证消息不丢失需结合$acks=all$、副本机制手动提交offset。实际应用中,生产者端配置重试$acks=all$是关键,Broker端确保副本健康,消费者端谨慎处理offset。 #### 2. **保证消息重复消费** 消息重复消费通常由消费者端offset管理当引起。Kafka通过以下机制避免重复消费: - **消费者配置**: - 关闭自动提交offset:设置$enable.auto.commit=false$,防止消费者在消息处理前就提交offset。如果处理失败但offset已提交,消费者重启后会跳过该消息,导致重复消费。 - 手动提交offset:在消息处理逻辑完成后,显式调用$consumer.commitSync()$或异步提交。这确保offset只提交一次处理成功。例如,在Java客户端中: ```java Properties props = new Properties(); props.put("enable.auto.commit", "false"); // 关闭自动提交 // 消费消息后手动提交 consumer.poll(Duration.ofMillis(100)).forEach(record -> { processMessage(record); // 处理消息 consumer.commitSync(); // 手动提交offset }); ``` 这能实现“恰好一次”(exactly-once)语义,避免重复[^3][^2]。 - **消费者组协调**: - Kafka使用消费者组(Consumer Group)来分配分区。组协调器(Group Coordinator)管理offset存储重平衡(Rebalance)。在重平衡期间(如消费者加入或离开),offset被可靠同步,防止因分区重新分配导致消息重复消费。 - 同时,消费者应实现幂等处理逻辑:即使消息重复消费,业务逻辑也能安全处理(例如,数据库操作使用唯一ID避免重复插入)[^3][^1]。 总结:避免重复消费的核心是手动提交offset消费者组管理。结合幂等性设计,Kafka能实现高可靠的消息传递。 #### 整体最佳实践 - **生产者**:配置$acks=all$、$retries>0$,并添加回调函数处理失败重试。 - **Broker**:确保$replication.factor \geq 3$$min.insync.replicas \geq 2$。 - **消费者**:始终使用手动提交offset,并处理消费异常。 通过这些机制,Kafka能在分布式环境中实现“至少一次”(at-least-once)或“恰好一次”(exactly-once)语义,具体取决于配置[^4][^5]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悬浮海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值