• Kafka核心技术与实战——11 | 无消息丢失配置怎么实现


    • 那 Kafka 到底在什么情况下才能保证消息不丢失呢?
      • 一句话概括,Kafka 只对“已提交”的消息(committed message)做有限度的持久化保证
      • 第一个核心要素是“已提交的消息”。
        • 什么是已提交的消息?当 Kafka 的若干个 Broker 成功地接收到一条消息并写入到日志文件后,它们会告诉生产者程序这条消息已成功提交。此时,这条消息在 Kafka 看来就正式变为“已提交”消息了。
        • 那为什么是若干个 Broker 呢?这取决于你对“已提交”的定义。你可以选择只要有一个 Broker 成功保存该消息就算是已提交,也可以是令所有 Broker 都成功保存该消息才算是已提交。不论哪种情况,Kafka 只对已提交的消息做持久化保证这件事情是不变的
      • 第二个核心要素就是“有限度的持久化保证”
        • 其实就是说 Kafka 不丢消息是有前提条件的。
        • 假如你的消息保存在 N 个 Kafka Broker 上,那么这个前提条件就是这 N 个 Broker 中至少有 1 个存活。只要这个条件成立,Kafka 就能保证你的这条消息永远不会丢失
    • “消息丢失”案例
      • 案例 1:生产者程序丢失数据
        • 我来描述一个场景:你写了一个 Producer 应用向 Kafka 发送消息,最后发现 Kafka 没有保存
        • 目前 Kafka Producer 是异步发送消息的,也就是说如果你调用的是 producer.send(msg) 这个 API,那么它通常会立即返回,但此时你不能认为消息发送已成功完成
        • 执行完一个操作后不去管它的结果是否成功。调用 producer.send(msg) 就属于典型的“fire and forget”。这个发送方式挺不靠谱吧,不过有些公司真的就是在使用这个 API 发送消息
        • 如果用这个方式,可能会有哪些因素导致消息没有发送成功呢?
          • 其实原因有很多,例如网络抖动,导致消息压根就没有发送到 Broker 端;
          • 或者消息本身不合格导致 Broker 拒绝接收(比如消息太大了,超过了 Broker 的承受能力)
        • 解决方案
          • Producer 永远要使用带有回调通知的发送 API,也就是说不要使用 producer.send(msg),而要使用 producer.send(msg, callback)
          • 一旦出现消息提交失败的情况,你就可以有针对性地进行处理
          • 如果是因为那些瞬时错误,那么仅仅让 Producer 重试就可以了;如果是消息不合格造成的,那么可以调整消息格式后再次发送
          • 发送失败真的没可能是由 Broker 端的问题造成的吗?当然可能!如果你所有的 Broker 都宕机了,那么无论 Producer 端怎么重试都会失败的,此时你要做的是赶快处理 Broker 端的问题
      • 案例 2:消费者程序丢失数据
        • 问题:Consumer 端丢失数据主要体现在 Consumer 端要消费的消息不见了。
          • Consumer 程序有个“位移”的概念,表示的是这个 Consumer 当前消费到的 Topic 分区的位置
          • 这里的“位移”类似于我们看书时使用的书签,它会标记我们当前阅读了多少页,下次翻书的时候我们能直接跳到书签页继续阅读
        • 解决方案
          • 维持先消费消息(阅读),再更新位移(书签)的顺序即可
          • 这种处理方式可能带来的问题是消息的重复处理,类似于同一页书被读了很多遍,但这不属于消息丢失的情形
        • 问题:其实还存在一种比较隐蔽的消息丢失场景
          • 对于 Kafka 而言,这就好比 Consumer 程序从 Kafka 获取到消息后开启了多个线程异步处理消息,而 Consumer 程序自动地向前更新位移
            • 假如其中某个线程运行失败了,它负责的消息没有被成功处理,但位移已经被更新了,因此这条消息对于 Consumer 而言实际上是丢失了
        • 解决方案
          • 如果是多线程异步处理消费消息,Consumer 程序不要开启自动提交位移,而是要应用程序手动提交位移
          • 提醒,单个 Consumer 程序使用多线程来消费消息说起来容易,写成代码却异常困难,因为你很难正确地处理位移的更新,也就是说避免无消费消息丢失很简单,但极易出现消息被消费了多次的情况
    • 最佳实践
      • 分享一下 Kafka 无消息丢失的配置,每一个其实都能对应上面提到的问题
      • 1、不要使用 producer.send(msg),而要使用 producer.send(msg, callback)。记住,一定要使用带有回调通知的 send 方法。
      • 2、设置 acks = all。acks 是 Producer 的一个参数,代表了你对“已提交”消息的定义。如果设置成 all,则表明所有副本 Broker 都要接收到消息,该消息才算是“已提交”。这是最高等级的“已提交”定义。
      • 3、设置 retries 为一个较大的值。这里的 retries 同样是 Producer 的参数,对应前面提到的 Producer 自动重试。当出现网络的瞬时抖动时,消息发送可能会失败,此时配置了 retries > 0 的 Producer 能够自动重试消息发送,避免消息丢失。
      • 4、设置 unclean.leader.election.enable = false。这是 Broker 端的参数,它控制的是哪些 Broker 有资格竞选分区的 Leader。如果一个 Broker 落后原先的 Leader 太多,那么它一旦成为新的 Leader,必然会造成消息的丢失。故一般都要将该参数设置成 false,即不允许这种情况的发生。
      • 5、设置 replication.factor >= 3。这也是 Broker 端的参数。其实这里想表述的是,最好将消息多保存几份,毕竟目前防止消息丢失的主要机制就是冗余。
      • 6、设置 min.insync.replicas > 1。这依然是 Broker 端参数,控制的是消息至少要被写入到多少个副本才算是“已提交”。设置成大于 1 可以提升消息持久性。在实际环境中千万不要使用默认值 1。
      • 7、确保 replication.factor > min.insync.replicas。如果两者相等,那么只要有一个副本挂机,整个分区就无法正常工作了。我们不仅要改善消息的持久性,防止数据丢失,还要在不降低可用性的基础上完成。推荐设置成 replication.factor = min.insync.replicas + 1。
      • 8、确保消息消费完成再提交。Consumer 端有个参数 enable.auto.commit,最好把它设置成 false,并采用手动提交位移的方式。就像前面说的,这对于单 Consumer 多线程处理的场景而言是至关重要的。
    • 开放讨论
      • 其实,Kafka 还有一种特别隐秘的消息丢失场景:增加主题分区
      • 当增加主题分区后,在某段“不凑巧”的时间间隔后,Producer 先于 Consumer 感知到新增加的分区,而 Consumer 设置的是“从最新位移处”开始读取消息,因此在 Consumer 感知到新分区前,Producer 发送的这些消息就全部“丢失”了,或者说 Consumer 无法读取到这些消息。严格来说这是 Kafka 设计上的一个小缺陷
  • 相关阅读:
    Java 实现 HDFS 文件基本操作
    [git] branch 分支操作
    Java基本语法-----java流程控制语句
    Java基本语法-----java运算符的优先级与结合性
    Java基本语法-----java数据类型的转换
    CSDN发表文章后老是待审核的原因
    Java基本语法-----java变量
    Java基本语法-----java进制的转换
    Java基本语法-----java常量
    Java基本语法-----java注释
  • 原文地址:https://www.cnblogs.com/minimalist/p/12876432.html
Copyright © 2020-2023  润新知