怎么防止Kafka 丢数据?

2 个回答

  • 野狐狸
    慢慢理解世界,慢慢更新自己。
    #### 前提条件 任何消息组件不丢数据都是在特定场景下一定条件的,kafka要保证消息不丢,有两个核心条件。 第一,必须是已提交的消息,即committed message。kafka对于committed message的定义是,生产者提交消息到broker,并等到多个broker确认并返回给生产者已提交的确认信息。而这多个broker是由我们自己来定义的,可以选择只要有一个broker成功保存该消息就算是已提交,也可以是令所有broker都成功保存该消息才算是已提交。不论哪种情况,kafka只对已提交的消息做持久化保证。 第二,也就是最基本的条件,虽然kafka集群是分布式的,但也必须保证有足够broker正常工作,才能对消息做持久化做保证。也就是说 kafka不丢消息是有前提条件的,假如你的消息保存在 N 个kafka broker上,那么这个前提条件就是这 N 个broker中至少有 1 个存活。只要这个条件成立,kafka就能保证你的这条消息永远不会丢失。 #### 如何保证消息不丢 一条消息从产生,到发送到kafka保存,到被取出消费,会有多个场景和流程阶段,可能会出现丢失情况,我们聊一下kafka通过哪些手段来保障消息不丢。 #### 生产端 Producer端可能会丢失消息。目前Kafka Producer是异步发送消息的,也就是说如果你调用的是producer.send(msg)这个API,那么它通常会立即返回,但此时你不保证消息发送已成功完成。可能会出现:网络抖动,导致消息压根就没有发送到Broker端;或者消息本身不合规导致Broker拒绝接收(比如消息太大了,超过了Broker的限制)。 实际上,使用producer.send(msg, callback)接口就能避免这个问题,根据回调,一旦出现消息提交失败的情况,就可以有针对性地进行处理。如果是因为那些瞬时错误,Producer重试就可以了;如果是消息不合规造成的,那么调整消息格式后再次发送。总之,处理发送失败的责任在Producer端而非Broker端。当然,如果此时broker宕机,那就另当别论,需要及时处理broker异常问题。 #### 消费端 Consumer端丢数据的情况,稍微复杂点。Consumer有个”位移“(offset)的概念,表示Consumer当前消费到topic分区的哪个位置。如图: 在这里插入图片描述 kafka通过先消费消息,后更新offset,来保证消息不丢失。但是这样可能会出现消息重复的情况,具体如何保证only-once,后续再单独分享。 当我们consumer端开启多线程异步去消费时,情况又会变得复杂一些。此时consumer自动地向前更新offset,假如其中某个线程运行失败了,它负责的消息没有被成功处理,但位移已经被更新了,因此这条消息对于consumer而言实际上是丢失了。这里的关键就在自动提交offset,如何真正地确认消息是否真的被消费,再进行更新offset。 这个问题的解决起来也简单:如果是多线程异步处理消费消息,consumer不要开启自动提交offset,consumer端程序自己来处理offset的提交更新。提醒你一下,单个consumer程序使用多线程来消费消息说起来容易,写成代码还是有点麻烦的,因为你很难正确地处理offset的更新,也就是说避免无消费消息丢失很简单,但极易出现消息被消费了多次的情况。 #### 实践配置 最后分享下kafka无消息丢失配置: producer端使用producer.send(msg, callback)带有回调的send方法。 设置acks = all。acks是Producer的一个参数,代表“已提交”消息的定义。如果设置成all,则表明所有Broker都要接收到消息,该消息才算是“已提交”。 设置retries为一个较大的值。同样是Producer的参数。当出现网络抖动时,消息发送可能会失败,此时配置了retries的Producer能够自动重试发送消息,尽量避免消息丢失。 设置unclean.leader.election.enable = false。这是Broker端的参数,在kafka版本迭代中社区也多次反复修改过他的默认值,之前比较具有争议。它控制哪些Broker有资格竞选分区的Leader。如果一个Broker落后原先的Leader太多,那么它一旦成为新的Leader,将会导致消息丢失。故一般都要将该参数设置成false。 设置replication.factor >= 3。这也是Broker端的参数。保存多份消息冗余,不多解释了。 设置min.insync.replicas > 1。Broker端参数,控制消息至少要被写入到多少个副本才算是“已提交”。设置成大于 1 可以提升消息持久性。在生产环境中不要使用默认值 1。确保replication.factor > min.insync.replicas。如果两者相等,那么只要有一个副本离线,整个分区就无法正常工作了。推荐设置成replication.factor = min.insync.replicas + 1。 确保消息消费完成再提交。Consumer端有个参数enable.auto.commit,最好设置成false,并自己来处理offset的提交更新。
    发布于 2022-03-14 21:13 •IP属地 湖北
    举报
    评论
    收藏

  • 澜夜师兄
    勇敢一点吧!我们不会比今年更年轻了。
    这块比较常见的一个场景,就是 Kafka 某个 broker 宕机,然后重新选举 partition 的 leader 。大家想想,要是此时其他的 follower 刚好还有些数据没有同步,结果此时 leader 挂了,然后选举某个 follower 成 leader 之后,不就少了一些数据?这就丢了一些数据啊。 此时一般是要求起码设置如下 4 个参数: 1. 给 topic 设置 replication.factor 参数:这个值必须大于 1,要求每个 partition 必须有 至少 2 个副本。 2. 在 Kafka 服务端设置 min.insync.replicas 参数:这个值必须大于 1,这个是 要求一个 leader 至少感知到有至少一个 follower 还跟自己保持联系,没掉队,这样才能确保 leader 挂了还有一个 follower 吧。 3. 在 producer 端设置 acks=all:这个是要求每条数据,必须是写入所有 replica 之后,才能认为是写成功了。 4. 在 producer 端设置 retries=MAX(很大很大很大的一个值,无限次重试的意思):这个是要求一旦写入失败,就无限重试,卡在这里了。 这样配置之后,至少在Kafka broker 端就可以保证在leader 所在 broker 发生故障,进行leader 切换时,数据不会丢失。
    发布于 2022-02-25 16:51 •IP属地 天津
    举报
    评论
    收藏