前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Kafka技术知识总结之五——Kafka的高可用性

Kafka技术知识总结之五——Kafka的高可用性

作者头像
剑影啸清寒
发布2020-07-08 14:48:01
1K0
发布2020-07-08 14:48:01
举报
文章被收录于专栏:琦小虾的Binary琦小虾的Binary

接上篇《Kafka技术知识总结之四——Kafka 再均衡》

五. 消息中间件的高可用性

5.1 消息中间件的高可用性

Kafka 实现高可用性的方式是进行 replication。对于 kafka,如果没有提供高可用性机制,一旦一个或多个 Broker 宕机,则宕机期间其上所有 Partition 都无法继续提供服务。若该 Broker永远不能再恢复,那么所有的数据也就将丢失,这是不可容忍的。所以 kafka 高可用性的设计也是进行 Replication。 Replica 的分布:为了尽量做好负载均衡和容错能力,需要将同一个 Partition 的 Replica 尽量分散到不同的机器。 Replica 的同步:当有很多 Replica 的时候,一般来说,对于这种情况有两个处理方法:

  • 同步复制:当 producer 向所有的 Replica 写入成功消息后才返回。一致性得到保障,但是延迟太高,吞吐率降低。
  • 异步复制:所有的 Replica 选取一个一个 leader,producer 向 leader 写入成功即返回(即生产者参数 acks = 1)。leader 负责将消息同步给其他的所有 Replica。但是消息同步一致性得不到保证,但是保证了快速的响应。

而 kafka 选取了一个折中的方式:ISR (in-sync replicas)。producer 每次发送消息,将消息发送给 leader,leader 将消息同步给他“信任”的“小弟们”就算成功,巧妙的均衡了确保数据不丢失以及吞吐率。具体步骤:

  1. 在所有的 Replica 中,leader 会维护一个与其基本保持同步的 Replica 列表,该列表称为ISR (in-sync Replica);每个 Partition 都会有一个 ISR,而且是由 leader 动态维护。
  2. 如果一个 replica 落后 leader 太多,leader 会将其剔除。如果另外的 replica 跟上脚步,leader 会将其加入。
  3. 同步:leader 向 ISR 中的所有 replica 同步消息,当收到所有 ISR 中 replica 的 ack 之后,leader 才 commit。
  4. 异步:收到同步消息的 ISR 中的 replica,异步将消息同步给 ISR 集合外的 replica。

参考地址: 《Kafka消息投递语义-消息不丢失,不重复,不丢不重》 《消息队列面试题要点》

  • 问题 1:使用 Kafka 的时候,你们怎么保证投递出去的消息一定不会丢失?
  • 问题 2:你们怎么保证投递出去的消息只有一条且仅仅一条,不会出现重复的数据?

上述问题换一种问法,可以翻译为**如何保证消息队列的幂等性?**这个问题可以认为是消息队列领域的基本问题。这个问题的回答可以根据具体的业务场景来答,没有固定的答案。

无论是哪种消息队列,造成重复消费原因其实都是类似的。正常情况下,消费者在消费消息的时候,消费完毕后,会发送一个确认消息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除。只是不同的消息队列发出的确认消息形式不同(例如 RabbitMQ 是发送一个 ACK 确认消息,RocketMQ 是返回一个 CONSUME_SUCCESS 成功标志),kafka 是通过**提交 offset 的方式**让消息队列知道自己已经消费过了。

造成重复消费的原因,就是因为网络传输等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将消息分发给其他的消费者。

注:关于重复消费,还有部分与第七章 Kafka 再均衡相关的内容。 参考地址:《记一次线上kafka一直rebalance故障》

如何解决?这个问题针对业务场景来答,分以下三种情况:

  • 实际使用方案:准备一个第三方介质,来做消费记录
    • 以 redis 为例,给消息分配一个全局 ID,只要消费过该消息,将 <id,message>以 K-V 形式写入 redis。消费者开始消费前,先去 redis 中查询有没有消费记录即可。
  • 数据库主键:拿到这个消息做数据库的 insert 操作,那就容易了,给这个消息做一个唯一的主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。
  • Redis Set 操作:拿到这个消息做 redis 的 set 的操作,那就容易了,不用解决,set 操作无论几次结果都是一样的,因为 set 操作本来就是幂等操作。

5.2 Kafka 消息投递语义

kafka 有三种消息投递语义:

  • At most Once:最多一次;消息不会重复,但可能丢失;
  • At least Once:最少一次;消息不会丢失,但可能重复;
  • Exactly Once:最佳情况,只且消费一次;消息不会重复,也不会丢失;

整体的消息投递语义由生产者、消费者两端同时保证。

5.3 Producer 生产者端

Producer 端保证消息投递重复性,是通过 Producer 的 acks 参数Broker 端的 min.insync.replicas 参数决定的。

Producer 端的 acks 参数值信息如下:

  • acks = 0:不等待任何响应的发送消息;
  • acks = 1leader 分片写消息成功,就返回响应给生产者;
  • acks = -1(all):要求 ISR 集合至少两个 Replica,而且必须全部 Replica 都写入成功,才返回响应给 Producer;
    • 无论 ISR 少于两个 Replica,或者不是全部 Replica 写入成功,都会抛出异常;

前面 Producer 的 acks = 1 可以保证写入 Leader 副本,对大部分情况没有问题。但如果刚刚一条消息写入 Leader,还没有把这条消息同步给其他 Replica,Leader 就挂了,那么这条消息也就丢失了。所以如果保证消息的完全投递,还是需要令 acks = all;

5.4 Broker 节点端

首先上面说到,为了配合 Producer acks 参数为 all,需要令 Broker 端参数 min.insync.replicas = 2; min.insync.replicas 参数是用来配合 Producer acks 参数的。因为如果 acks 设置为 all,但某个 Topic 只有 leader 一个 Replica(或者某个 Kafka 集群中由于同步很慢,导致所有 follower 全部被剔除 ISR 集合),这样 acks = -1 就演变成了 acks = 1。 所以需要 Broker 端设置 min.insync.replicas 参数:当参数值为 2 时,如果副本数小于 2 个,会抛出异常。

注:然而在笔者的使用环境中,订阅是 Kafka 主要的使用场景之一,方式是对于想要订阅的某个 Topic,每个用户创建并独享一个不会重复的消费组。所以这样的情况下,环境下的 min.insync.replicas 只能等于 1;

除此之外,broker 端还有一个需要注意的参数 unclean.leader.election.enable。该参数为 true 的时候,表示在 leader 下线的时候,可以从非 ISR 集合中选举出新的 Leader。这样的话可能会造成数据的丢失。所以如果需要在 Broker 端的 unclean.leader.election.enable 设置为 false。

5.5 Consumer 消费者端

Consumer 端比较麻烦,原因是需要考虑到某个 Consumer 宕机后,同 Consumer Group 会发生负载均衡,同 Group 其他的 Consumer 会重新接管并继续消费。

假设两种场景:

第一个场景,Consumer 先提交 offset,再处理消息。代码如下:

List<String> messages = consumer.poll();
consumer.commitOffset();
processMsg(messages);

这种情形下,提交 offset 成功,但处理消息失败,同时当前 Consumer 宕机,这时候发生负载均衡,其他 Consumer 从已经提交的 offset 之后继续消费。这样的情况保证了 at most once 的消费语义,当然也可能会丢消息。

第二个场景,Consumer 先处理消息,再提交 offset。代码如下:

List<String> messages = consumer.poll();
processMsg(messages);
consumer.commitOffset();

这种情形下,消息处理成功,提交 offset 失败,同时当前 Consumer 宕机,这时候发生负载均衡,其他 Consumer 依旧从同样的 offset 拉取消息消费。这样的情况保证了 at least once 的消费语义,可能会重复消费消息。

上述机制的保证都不是直接一个配置可以解决的,而是 Consumer 端代码的处理先后顺序问题完成的。

注:关于 Kafka 解耦作用的思考: 注册中心可以将服务于服务之间解耦,但 Kafka 也可以通过相同的 topic 的消息传递实现业务的解耦。这两种形式都可以实现解耦,但笔者个人理解:

  • 注册中心通过请求 -> 响应的模式,等待其他服务处理结果完毕之后的响应;
  • Kafka 的将消息从生产者投递,消费者接收,但消费者的消费结果通常生产者并不需要的,生产者只需要确保将消息投递到 Kafka Broker 节点即可。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-07-07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 五. 消息中间件的高可用性
    • 5.1 消息中间件的高可用性
      • 5.2 Kafka 消息投递语义
        • 5.3 Producer 生产者端
          • 5.4 Broker 节点端
            • 5.5 Consumer 消费者端
            相关产品与服务
            消息队列 CMQ 版
            消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档