7、无消息丢失配置怎么实现?

无消息丢失配置怎么实现?

  • 1、Kafka 保障消息不丢失的限度
    • 核心要素一:“已提交的消息”
    • 核心要素一:“有时限”
  • 2、“消息丢失” 案例
    • 案例 1:生产者程序丢失数据
    • 案例 2:消费者程序丢失数据
  • 3、最佳实践

1、Kafka 保障消息不丢失的限度

首先要明确,在 Kafka 的世界里什么才算是消息丢失,即 Kafka 在什么情况下能保证消息不丢失。

一句话概括,Kafka 只对 “已提交” 的消息(committed message)做有时限的持久化保证。

核心要素一:“已提交的消息”

什么是已提交的消息?当一条消息被 Kafka 某分区的若干个副本成功接收到并写入到日志文件后,它们会告诉生产者程序这条消息已成功提交。此时,这条消息在 Kafka 看来就正式变为 “已提交” 消息了。

为什么是若干个副本呢?这取决于你对 “已提交” 的定义。你可以选择只要有一个 Broker 成功保存该消息就算是已提交,也可以是令所有 Broker 都成功保存该消息才算是已提交。不论哪种情况,Kafka 只对已提交的消息做持久化保证这件事情是不变的。

核心要素一:“有时限”

Kafka 保存消息有时长限制

2、“消息丢失” 案例

这里是带引号的消息丢失哦,其实这些情况只是我们冤枉了 Kafka 而已。

案例 1:生产者程序丢失数据

场景描述:有个 Producer 应用向 Kafka 发送消息,最后发现 Kafka 没有保存。这种情况是什么导致的呢?一定是 Kafka 把消息丢了么?

目前 Kafka Producer 是异步发送消息的,也就是说如果你调用的是 producer.send (msg) 这个 API,那么它通常会立即返回,但此时你不能认为消息发送已成功完成。

这种发送方式有个有趣的名字,叫 “fire and forget”,翻译一下就是 “发射后不管”。这个术语原本属于导弹制导领域,后来被借鉴到计算机领域中,它的意思是,执行完一个操作后不去管它的结果是否成功。调用 producer.send (msg) 就属于典型的 “fire and forget”,因此如果出现消息丢失,我们是无法知晓的。这个发送方式挺不靠谱吧,不过有些公司真的就是在使用这个 API 发送消息。

如果用这个方式,可能会有哪些因素导致消息没有发送成功呢?其实原因有很多,例如网络抖动,导致消息压根就没有发送到 Broker 端;或者消息本身不合格导致 Broker 拒绝接收(比如消息太大了,超过了 Broker 的承受能力)等。这么来看,让 Kafka “背锅” 就有点冤枉它了。就像前面说过的,Kafka 不认为消息是已提交的,因此也就没有 Kafka 丢失消息这一说了。

不过,就算不是 Kafka 的 “锅”,我们也要解决这个问题吧。实际上,解决此问题的方法非常简单:Producer 永远要使用带有回调通知的发送 API,也就是说不要使用 producer.send (msg),而要使用 producer.send (msg, callback)。不要小瞧这里的 callback(回调),它能准确地告诉你消息是否真的提交成功了。一旦出现消息提交失败的情况,你就可以有针对性地进行处理。

举例来说,如果是因为那些瞬时错误,那么仅仅让 Producer 重试就可以了;如果是消息不合格造成的,那么可以调整消息格式后再次发送。总之,处理发送失败的责任在 Producer 端而非 Broker 端。

你可能会问,发送失败真的没可能是由 Broker 端的问题造成的吗?当然可能!如果你所有的 Broker 都宕机了,那么无论 Producer 端怎么重试都会失败的,此时你要做的是赶快处理 Broker 端的问题。但之前说的核心论据在这里依然是成立的:Kafka 依然不认为这条消息属于已提交消息,故对它不做任何持久化保证。

案例 2:消费者程序丢失数据

Consumer 端丢失数据主要体现在 Consumer 端要消费的消息不见了。Consumer 程序有个 “位移” 的概念,表示的是这个 Consumer 当前消费到的 Topic 分区的位置。

这里的 “位移” 类似于我们看书时使用的书签,它会标记我们当前阅读了多少页,下次翻书的时候我们能直接跳到书签页继续阅读。

正确使用书签有两个步骤:第一步是读书,第二步是更新书签页。如果这两步的顺序颠倒了,就可能出现这样的场景:当前的书签页是第 90 页,我先将书签放到第 100 页上,之后开始读书。当阅读到第 95 页时,我临时有事中止了阅读。那么问题来了,当我下次直接跳到书签页阅读时,我就丢失了第 96~99 页的内容,即这些消息就丢失了。

同理,Kafka 中 Consumer 端的消息丢失就是这么一回事。要对抗这种消息丢失,办法很简单:维持先消费消息(阅读),再更新位移(书签)的顺序即可。这样就能最大限度地保证消息不丢失。

当然,这种处理方式可能带来的问题是消息的重复处理,类似于同一页书被读了很多遍,但这不属于消息丢失的情形。在专栏后面的内容中,我会跟你分享如何应对重复消费的问题。

3、最佳实践

看完这两个案例之后,我来分享一下 Kafka 无消息丢失的配置,每一个其实都能对应上面提到的问题。

  • 不要使用 producer.send (msg),而要使用 producer.send (msg, callback)。记住,一定要使用带有回调通知的 send 方法。
  • 设置 acks = all。acks 是 Producer 的一个参数,代表了你对 “已提交” 消息的定义。如果设置成 all,则表明所有副本 Broker 都要接收到消息,该消息才算是 “已提交”。这是最高等级的 “已提交” 定义。
  • 确保消息消费完成再提交。Consumer 端有个参数 enable.auto.commit,最好把它设置成 false,并采用手动提交位移的方式。就像前面说的,这对于单 Consumer 多线程处理的场景而言是至关重要的。

你可能感兴趣的:(#,Kafka,kafka,分布式,处理消息丢失)