Kafka大厂高频面试题:在保证高性能、高吞吐的同时保证高可用性

news2024/12/27 12:53:27

Kafka的消息传输保障机制非常直观。当producer向broker发送消息时,一旦这条消息被commit,由于副本机制(replication)的存在,它就不会丢失。但是如果producer发送数据给broker后,遇到的网络问题而造成通信中断,那producer就无法判断该条消息是否已经提交(commit)。虽然Kafka无法确定网络故障期间发生了什么,但是producer可以retry多次,确保消息已经正确传输到broker中,所以目前Kafka实现的是at least once。

一、幂等性

1.场景

所谓幂等性,就是对接口的多次调用所产生的结果和调用一次是一致的。生产者在进行重试的时候有可能会重复写入消息,而使用Kafka的幂等性功能就可以避免这种情况。

幂等性是有条件的:

  • 只能保证 Producer 在单个会话内不丢不重,如果 Producer 出现意外挂掉再重启是无法保证的(幂等性情况下,是无法获取之前的状态信息,因此是无法做到跨会话级别的不丢不重);

  • 幂等性不能跨多个 Topic-Partition,只能保证单个 partition 内的幂等性,当涉及多个Topic-Partition 时,这中间的状态并没有同步。

Producer 使用幂等性的示例非常简单,与正常情况下 Producer 使用相比变化不大,只需要把Producer 的配置 enable.idempotence 设置为 true 即可,如下所示:

 
 

Properties props = new Properties(); props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, "true"); props.put("acks", "all"); // 当 enable.idempotence 为 true,这里默认为 all props.put("bootstrap.servers", "localhost:9092"); props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); KafkaProducer producer = new KafkaProducer(props); producer.send(new ProducerRecord(topic, "test");

二、事务

1.场景

幂等性并不能跨多个分区运作,而事务可以弥补这个缺憾,事务可以保证对多个分区写入操作的原子性。操作的原子性是指多个操作要么全部成功,要么全部失败,不存在部分成功部分失败的可能。

为了实现事务,应用程序必须提供唯一的transactionalId,这个参数通过客户端程序来进行设定。

见代码库: com.heima.kafka.chapter7.ProducerTransactionSend

 
 

properties.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, transactionId);

2.前期准备

事务要求生产者开启幂等性特性,因此通过将transactional.id参数设置为非空从而开启事务特性的同时需要将 ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG设置为true(默认值为true),如果显示设置为false,则会抛出异常。

KafkaProducer提供了5个与事务相关的方法,详细如下:

 
 

//初始化事务,前提是配置了transactionalId public void initTransactions() //开启事务 public void beginTransaction() //为消费者提供事务内的位移提交操作 public void sendOffsetsToTransaction(Map<TopicPartition, OffsetAndMetadata> offsets, String consumerGroupId) //提交事务 public void commitTransaction() //终止事务,类似于回滚 public void abortTransaction()

3.案例解析

见代码库: com.heima.kafka.chapter7.ProducerTransactionSend

消息发送端

 
 

/** * Kafka Producer事务的使用 */ public class ProducerTransactionSend { public static final String topic = "topic-transaction"; public static final String brokerList = "localhost:9092"; public static final String transactionId = "transactionId"; public static void main(String[] args) { Properties properties = new Properties(); properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, brokerList); properties.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, transactionId); KafkaProducer<String, String> producer = new KafkaProducer<> (properties); producer.initTransactions(); producer.beginTransaction(); try { //处理业务逻辑并创建ProducerRecord ProducerRecord<String, String> record1 = new ProducerRecord<>(topic, "msg1"); producer.send(record1); ProducerRecord<String, String> record2 = new ProducerRecord<>(topic, "msg2"); producer.send(record2); ProducerRecord<String, String> record3 = new ProducerRecord<>(topic, "msg3"); producer.send(record3); //处理一些其它逻辑 producer.commitTransaction(); } catch (ProducerFencedException e) { producer.abortTransaction(); } producer.close(); } }

模拟事务回滚案例

 
 

try { //处理业务逻辑并创建ProducerRecord ProducerRecord<String, String> record1 = new ProducerRecord<>(topic, "msg1"); producer.send(record1); //模拟事务回滚案例 System.out.println(1/0); ProducerRecord<String, String> record2 = new ProducerRecord<>(topic, "msg2"); producer.send(record2); ProducerRecord<String, String> record3 = new ProducerRecord<>(topic, "msg3"); producer.send(record3); //处理一些其它逻辑 producer.commitTransaction(); } catch (ProducerFencedException e) { producer.abortTransaction(); }

从上面案例中,msg1发送成功之后,出现了异常事务进行了回滚,则msg1消费端也收不到消息。

三、控制器

在Kafka集群中会有一个或者多个broker,其中有一个broker会被选举为控制器(Kafka Controller),它负责管理整个集群中所有分区和副本的状态。当某个分区的leader副本出现故障时,由控制器负责为该分区选举新的leader副本。当检测到某个分区的ISR集合发生变化时,由控制器负责通知所有broker更新其元数据信息。当使用kafka-topics.sh脚本为某个topic增加分区数量时,同样还是由控制器负责分区的重新分配。

Kafka中的控制器选举的工作依赖于Zookeeper,成功竞选为控制器的broker会在Zookeeper中创建/controller这个临时(EPHEMERAL)节点,此临时节点的内容参考如下:

1.ZooInspector管理

  • 使用zookeeper图形化的客户端工具(ZooInspector)提供的jar来进行管理,启动如下:

  1. 定位到jar所在目录

  2. 运行jar文件 java -jar zookeeper-dev-ZooInspector.jar

  3. 连接Zookeeper

编辑切换为居中

添加图片注释,不超过 140 字(可选)

 
 

{"version":1,"brokerid":0,"timestamp":"1529210278988"}

编辑切换为居中

添加图片注释,不超过 140 字(可选)

其中version在目前版本中固定为1,brokerid表示称为控制器的broker的id编号,timestamp表示竞选称为控制器时的时间戳。

在任意时刻,集群中有且仅有一个控制器。每个broker启动的时候会去尝试去读取/controller节点的brokerid的值,如果读取到brokerid的值不为-1,则表示已经有其它broker节点成功竞选为控制器,所以当前broker就会放弃竞选;如果Zookeeper中不存在/controller这个节点,或者这个节点中的数据异常,那么就会尝试去创建/controller这个节点,当前broker去创建节点的时候,也有可能其他broker同时去尝试创建这个节点,只有创建成功的那个broker才会成为控制器,而创建失败的broker则表示竞选失败。每个broker都会在内存中保存当前控制器的brokerid值,这个值可以标识为activeControllerId。

Zookeeper中还有一个与控制器有关的/controller_epoch节点,这个节点是持久(PERSISTENT)节点,节点中存放的是一个整型的controller_epoch值。controller_epoch用于记录控制器发生变更的次数,即记录当前的控制器是第几代控制器,我们也可以称之为“控制器的纪元”。

编辑切换为居中

添加图片注释,不超过 140 字(可选)

controller_epoch的初始值为1,即集群中第一个控制器的纪元为1,当控制器发生变更时,没选出一个新的控制器就将该字段值加1。每个和控制器交互的请求都会携带上controller_epoch这个字段,如果请求的controller_epoch值小于内存中的controller_epoch值,则认为这个请求是向已经过期的控制器所发送的请求,那么这个请求会被认定为无效的请求。如果请求的controller_epoch值大于内存中的controller_epoch值,那么则说明已经有新的控制器当选了。由此可见,Kafka通过controller_epoch来保证控制器的唯一性,进而保证相关操作的一致性。

具备控制器身份的broker需要比其他普通的broker多一份职责,具体细节如下:

  1. 监听partition相关的变化。

  2. 监听topic相关的变化。

  3. 监听broker相关的变化。

  4. 从Zookeeper中读取获取当前所有与topic、partition以及broker有关的信息并进行相应的管理。

四、可靠性保证

  1. 可靠性保证:确保系统在各种不同的环境下能够发生一致的行为

  2. Kafka的保证

  3. 保证分区消息的顺序如果使用同一个生产者往同一个分区写入消息,而且消息B在消息A之后写入那么Kafka可以保证消息B的偏移量比消息A的偏移量大,而且消费者会先读取消息A再读取消息B

  4. 只有当消息被写入分区的所有同步副本时(文件系统缓存),它才被认为是已提交

  5. 生产者可以选择接收不同类型的确认,控制参数 acks

  6. 只要还有一个副本是活跃的,那么已提交的消息就不会丢失

  7. 消费者只能读取已经提交的消息

1. 失效副本

怎么样判定一个分区是否有副本是处于同步失效状态的呢?从Kafka 0.9.x版本开始通过唯一的一个参数replica.lag.time.max.ms(默认大小为10,000)来控制,当ISR中的一个follower副本滞后leader副本的时间超过参数replica.lag.time.max.ms指定的值时即判定为副本失效,需要将此follower副本剔出除ISR之外。具体实现原理很简单,当follower副本将leader副本的LEO(Log End Offset,每个分区最后一条消息的位置)之前的日志全部同步时,则认为该follower副本已经追赶上leader副本,此时更新该副本的lastCaughtUpTimeMs标识。Kafka的副本管理器(ReplicaManager)启动时会启动一个副本过期检测的定时任务,而这个定时任务会定时检查当前时间与副本的lastCaughtUpTimeMs差值是否大于参数replica.lag.time.max.ms指定的值。千万不要错误地认为follower副本只要拉取leader副本的数据就会更新lastCaughtUpTimeMs,试想当leader副本的消息流入速度大于follower副本的拉取速度时,follower副本一直不断的拉取leader副本的消息也不能与leader副本同步,如果还将此follower副本置于ISR中,那么当leader副本失效,而选取此follower副本为新的leader副本,那么就会有严重的消息丢失。

2.副本复制

Kafka 中的每个主题分区都被复制了 n 次,其中的 n 是主题的复制因子(replication factor)。这允许Kafka 在集群服务器发生故障时自动切换到这些副本,以便在出现故障时消息仍然可用。Kafka 的复制是以分区为粒度的,分区的预写日志被复制到 n 个服务器。 在 n 个副本中,一个副本作为 leader,其他副本成为 followers。顾名思义,producer 只能往 leader 分区上写数据(读也只能从 leader 分区上进行),followers 只按顺序从 leader 上复制日志。

一个副本可以不同步Leader有如下几个原因 慢副本:在一定周期时间内follower不能追赶上leader。最常见的原因之一是I / O瓶颈导致follower追加复制消息速度慢于从leader拉取速度。 卡住副本:在一定周期时间内follower停止从leader拉取请求。follower replica卡住了是由于GC暂停或follower失效或死亡。

新启动副本:当用户给主题增加副本因子时,新的follower不在同步副本列表中,直到他们完全赶上了leader日志。

如何确定副本是滞后的

 
 

replica.lag.max.messages=4

编辑切换为居中

添加图片注释,不超过 140 字(可选)

在服务端现在只有一个参数需要配置replica.lag.time.max.ms。这个参数解释replicas响应partition leader的最长等待时间。检测卡住或失败副本的探测——如果一个replica失败导致发送拉取请求时间间隔超过replica.lag.time.max.ms。Kafka会认为此replica已经死亡会从同步副本列表从移除。检测慢副本机制发生了变化——如果一个replica开始落后leader超过replica.lag.time.max.ms。Kafka会认为太缓慢并且会从同步副本列表中移除。除非replica请求leader时间间隔大于replica.lag.time.max.ms,因此即使leader使流量激增和大批量写消息。Kafka也不会从同步副本列表从移除该副本。

1.Leader Epoch引用

数据丢失场景

编辑切换为居中

添加图片注释,不超过 140 字(可选)

数据出现不一致场景

编辑切换为居中

添加图片注释,不超过 140 字(可选)

2.Kafka 0.11.0.0.版本解决方案

造成上述两个问题的根本原因在于HW值被用于衡量副本备份的成功与否以及在出现failture时作为日志截断的依据,但HW值得更新是异步延迟的,特别是需要额外的FETCH请求处理流程才能更新,故这中间发生的任何崩溃都可能导致HW值的过期。鉴于这些原因,Kafka 0.11引入了leader epoch来取代HW值。Leader端多开辟一段内存区域专门保存leader的epoch信息,这样即使出现上面的两个场景也能很好地规避这些问题。

所谓leader epoch实际上是一对值:(epoch,offset)。epoch表示leader的版本号,从0开始,当leader变更过1次时epoch就会+1,而offset则对应于该epoch版本的leader写入第一条消息的位移。因此假设有两对值:

  1. (0, 0)

  2. (1, 120)

则表示第一个leader从位移0开始写入消息;共写了120条[0, 119];而第二个leader版本号是1,从位移120处开始写入消息。

leader broker中会保存这样的一个缓存,并定期地写入到一个checkpoint文件中。

避免数据丢失:

编辑切换为居中

添加图片注释,不超过 140 字(可选)

避免数据不一致

编辑切换为居中

添加图片注释,不超过 140 字(可选)

六、消息重复的场景及解决方案

1.生产者端重复

生产发送的消息没有收到正确的broke响应,导致producer重试。

producer发出一条消息,broke落盘以后因为网络等种种原因发送端得到一个发送失败的响应或者网络中断,然后producer收到一个可恢复的Exception重试消息导致消息重复。

解决方案:

  1. 启动kafka的幂等性

要启动kafka的幂等性,无需修改代码,默认为关闭,需要修改配置文件:enable.idempotence=true 同时要求 ack=all 且 retries>1。

  1. ack=0,不重试。

可能会丢消息,适用于吞吐量指标重要性高于数据丢失,例如:日志收集。

2.消费者端重复

  1. 根本原因

数据消费完没有及时提交offset到broker。

解决方案

  1. 取消自动自动提交

每次消费完或者程序退出时手动提交。这可能也没法保证一条重复。

  1. 下游做幂等

一般的解决方案是让下游做幂等或者尽量每消费一条消息都记录offset,对于少数严格的场景可能需要把offset或唯一ID,例如订单ID和下游状态更新放在同一个数据库里面做事务来保证精确的一次更新或者在下游数据表里面同时记录消费offset,然后更新下游数据的时候用消费位点做乐观锁拒绝掉旧位点的数据更新。

七、__consumer_offsets

_consumer_offsets是一个内部topic,对用户而言是透明的,除了它的数据文件以及偶尔在日志中出现这两点之外,用户一般是感觉不到这个topic的。不过我们的确知道它保存的是Kafka新版本consumer的位移信息。

1.何时创建

一般情况下,当集群中第一有消费者消费消息时会自动创建主题__consumer_offsets,分区数可以通过 offsets.topic.num.partitions参数设定,默认值为50,如下

编辑切换为居中

添加图片注释,不超过 140 字(可选)

2.解析分区

见代码库: com.heima.kafka.chapter7.ConsumerOffsetsAnalysis

获取所有分区

编辑切换为居中

添加图片注释,不超过 140 字(可选)

总结

本章主要讲解了Kafka相关稳定性的操作,包括幂等性、事务的处理,同时对可靠性保证与一致性保证做了讲解,讲解了消息重复以及解决方案。

                                 资源获取:

大家点赞、收藏、关注、评论啦 、查看👇🏻👇🏻👇🏻微信公众号获取联系方式👇🏻👇🏻👇🏻

 精彩专栏推荐订阅:下方专栏👇🏻👇🏻👇🏻👇🏻

每天学四小时:Java+Spring+JVM+分布式高并发,架构师指日可待

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/111450.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

火山引擎 DataTester 上线“流程画布”功能,支持组合型 A/B 实验分析

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 在精细化运营的时代&#xff0c;运营活动同样需要有精细化的策略&#xff0c;例如在年末大促活动中&#xff0c;设计 APP 弹窗提醒、满减、会员领券时&#xff0c;我…

C#语言实例源码系列-实现批量更改文件名称大小写或扩展名

专栏分享点击跳转>Unity3D特效百例点击跳转>案例项目实战源码点击跳转>游戏脚本-辅助自动化点击跳转>Android控件全解手册 &#x1f449;关于作者 众所周知&#xff0c;人生是一个漫长的流程&#xff0c;不断克服困难&#xff0c;不断反思前进的过程。在这个过程中…

DeepLearning_Note

这里写目录标题深度学习框架深度学习开发万能公式模型的网络设计和开发&#xff1a;激活函数&#xff1a;几个数据参数&#xff1a;神经网络的输出零碎知识点&#xff1a;深度学习框架 深度学习开发万能公式 ① 问题定义 ② Paddle.vision.datasets&#xff08;内置数据集&am…

【ROS参数服务器】

参数服务器是以共享方式实现不同节点间数据交互的通信方式。主要用于存储多节点共享的数据&#xff0c;类似于全局变量。ROS中的参数服务器主要包含三个角色&#xff0c;分别是ROS Master(节点管理者)、Talker(参数设置者)、Listener(参数使用者)&#xff0c;其中Talker和Liste…

创新京东T7开创“新算法宝典”,图文并茂,全新演绎,太酷了

导言 算法是一门学问&#xff0c;但却总遭到一些程序员的冷落。现在的开发人员&#xff0c;更热衷于编程语言的修炼&#xff0c;以应付面试需求时的需要&#xff0c;所以对算法的学习&#xff0c;稍许忽略了些。实际上&#xff0c;近些年来&#xff0c;各互联网公司对于算法的…

【知识梳理】前端路由的两种模式

一、概述 这是几年前写的的一篇文章&#xff0c;发在了简书上面&#xff0c;现在看来仍然有一些不足&#xff0c;所以再次整理一下发在掘金。 二、什么是单页面应用&#xff08;SPA&#xff09;&#xff1f; 首先我们需要了解一下前置的基础知识————SPA&#xff08;单页面…

S3 #DooTrader 经典组冠军以良好盘感,创下近 900% 收益率摘得桂冠

本届 S3 #DooTrader 慈善杯全球交易大赛现已经进入白热化阶段&#xff0c;第二轮赛事冲刺在即&#xff0c;各位选手摩拳擦掌争取赢得最终的丰厚奖金。目前&#xff0c;领先的选手调整策略和仓位&#xff0c;以保持排位优势。我们看到现阶段经典组 TOP 1 选手已经创造了 1,300% …

WMS类图分析-android12

为什么要分析类图&#xff1f; WMS是一个复杂的模块&#xff0c;就像一个很大的家族&#xff0c;里面有各种角色&#xff0c;认识类图就像是认识WMS模块中的各个角色&#xff0c;不先把人认清楚了&#xff0c;怎么更好的理解他们之间的交互&#xff1f; 我觉得&#xff0c;这…

vue+antd搭建后台管理界面模版(PC端),适配中文、英文、日文 mock数据,开箱即用

vueantd搭建后台管理界面模版&#xff08;PC端&#xff09; 完整代码下载地址&#xff1a;vueantd搭建后台管理界面模版&#xff08;PC端&#xff09; 技术栈 vue2 vuex vue-router webpack ES6/7 axios antd 阿里图标iconfont 项目预览 http://nmgwap.gitee.io/vue…

【软件工程】实验4:校园二手物品交易过程的UI设计

文章目录校园二手物品交易过程的UI设计通过“用户画像”对用户群体进行分析校园二手物品交易过程UI设计&#xff08;Figma&#xff09;校园二手物品交易过程的UI设计 通过“用户画像”对用户群体进行分析 大学校园交易市场特点&#xff1a; 容量大。随着我国高等教育近年来的连…

Kubernetes部署_使用kubernetes部署Mysql主从结构(Kubernetes工作实践类)

文章目录一、前言二、实际操作步骤1&#xff1a;编写namespace脚本步骤2&#xff1a;编写configmap脚本步骤3&#xff1a;编写secret脚本(用来存放mysql密码)步骤4&#xff1a;编写initContainer脚本步骤5&#xff1a;编写StorageClass相关脚本1&#xff09;权限设置&#xff1…

干货分享 | To B业务的用户运营五要点

随着产业互联网格局的逐渐深化&#xff0c;近年来&#xff0c;To B业务逐渐被互联网改变和赋能。为了更高效地获客和服务&#xff0c;更多的运营手段逐渐被运用在To B业务之中&#xff0c;而To B运营也变得越来越重要。 作为一家To B企业&#xff0c;AdBright常常收到网友的提问…

YOLOV3论文学习

YOLOv3论文链接&#xff1a;https://pjreddie.com/media/files/papers/YOLOv3.pdf 综述 一、摘要 1、320*320的YOLOv3推理时间22ms&#xff0c;准确率28.2mAP&#xff0c;达到了SSD的精确度&#xff0c;推理速度却快了三倍。 2、基于.5mAp Iou 的YOLOv3的检测效果还比较不错&a…

Python使用Pandas导入数据库sql

Python使用Pandas导入数据库sql一、前言二、准备工作三、从数据库导入数据到Pandas一、前言 对于关系数据库的访问&#xff0c;Python社区已经制定出一个标准&#xff0c;称为Python Database API Specification。Mysql&#xff0c;Oracal等特定数据库模块都遵从这一规范&…

QT 学习笔记(十一)

文章目录一、绘图设备1. QPixmap1.1 QPixmap 简介1.2 QPixmap 演示2. QBitmap2.1 QBitmap 简介2.2 QBitmap 演示见 QPixmap 和 QBitmap 的区别。3. QImage3.1 QImage 简介3.2 QImage 演示4. QPicture4.1 QPicture 简介4.2 QPicture 演示二、QPixmap 和 QBitmap 的区别1. widget…

Python正在消亡?致命弱点是否会让Python被新语言取代?

被业界称为“瑞士军刀”的编程语言&#xff0c;可能会被更适合该任务的其他语言取代吗&#xff1f; 自从1990年代初Python发布以来&#xff0c;它引起了很多热议。当然&#xff0c;编程社区花了至少20年的时间才逐渐注意到它的存在&#xff0c;而当它一旦开始流行起来&#xf…

Web入门开发【一】- Web开发介绍

欢迎来到霍大侠的小院&#xff0c;我们来学习Web入门开发的系列课程。 首先我们来了解下这个课程能学到什么&#xff1f; 1、你将可以掌握Web网站的开发全过程。 2、了解基础的HTML&#xff0c;CSS&#xff0c;JavaScript语言。 3、开发自己的第一个网站。 4、认识很多对编…

UML类关系

1、聚合关系&#xff08;aggregation&#xff09; 用空心菱形箭头表示&#xff0c;整体和部分有各自的生命周期。部分可以属于多个整体对象! class Student; class Class { private: Student s; public: void set_student(Student s) { this.s s; } }; student() { print(“c…

电脑商城网站

开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a; 作为一个网上商城系统&#xff0c;就应该做到能提供强大的业务支持功能&#xff0c;系统能实现用户的注册功能、登录 功…

整理各种Vue项目在IE浏览器白屏报错 SCRIPT1002:语法错误

目录 一、关于 sockjs-client 依赖包 二、关于 highlight 依赖包 三、关于 swiper 依赖包 四、IE 不支持 ES6 语法 五、第三方插件引入导致 六、本地环境正常&#xff0c;生产环境仍旧白屏 这篇文章主要介绍了 Vue 项目在 IE 浏览器显示白屏并报错 SCRIPT1002: 语法错误 …