java面试编译原理,Java工程师的进阶之路-Kafka篇,中科创达java面试经验

3.2. Follower 副本

除了 Leader 副本以外的副本统称为 Follower 副本,Follower 不对外提供服务。下面是 Leader 副本的工作方式:
image
需要注意以下几点:

Kafka 中,Follower 副本也就是追随者副本是不对外提供服务的。这就是说,任何一个追随者副本都不能响应消费者和生产者的请求。所有的请求都是由领导者副本来处理。或者说,所有的请求都必须发送到 Leader 副本所在的 broker 中,Follower 副本只是用做数据拉取,采用异步拉取的方式,并写入到自己的提交日志中,从而实现与 Leader 的同步;

当 Leader 副本所在的 broker 宕机后,Kafka 依托于 ZooKeeper 提供的监控功能能够实时感知到,并开启新一轮的选举,从追随者副本中选一个作为 Leader。如果宕机的 broker 重启完成后,该分区的副本会作为 Follower 重新加入。

3.3. Follower 和 Leader 副本同步

Leader 的另一个任务是搞清楚哪个 Follower 的状态与自己是一致的。Follower 为了保证与 Leader 的状态一致,在有新消息到达之前先尝试从 Leader 那里复制消息。为了与 Leader 保持一致,Follower 向 Leader 发起获取数据的请求,这种请求与消费者为了读取消息而发送的信息是一样的。

Follower 向 Leader 发送消息的过程是这样的,先请求消息1,然后再接收到消息1,在时候到请求1之后,发送请求2,在收到领导者给发送给跟随者之前,跟随者是不会继续发送消息的。这个过程如下:
imageFollower 副本在收到响应消息前,是不会继续发送消息,这一点很重要。通过查看每个 Follower 请求的最新偏移量, Leader 就会知道每个 Follower 复制的进度。

如果 Follower 在10s 内没有请求任何消息,或者虽然 Follower 已经发送请求,但是在10s 内没有收到消息,就会被认为是不同步的。如果一个副本没有与 Leader 同步,那么在 Leader 掉线后,这个副本将不会称为 Leader ,因为这个副本的消息不是全部的。

与之相反的,如果 Follower 同步的消息和 Leader 副本的消息一致,那么这个 Follower 副本又被称为同步的副本。也就是说,如果 Leader 掉线,那么只有同步的副本能够称为 Leader

副本机制的好处是什么?

  • 能够立刻看到写入的消息,就是你使用生产者 API 成功向分区写入消息后,马上使用消费者就能读取刚才写入的消息;
  • 能够实现消息的幂等性,啥意思呢?就是对于生产者产生的消息,在消费者进行消费的时候,它每次都会看到消息存在,并不会存在消息不存在的情况;

3.4. 同步复制和异步复制

既然 Leader 副本和 Follower 副本是 发送 - 等待机制 的,这是一种同步的复制方式,那么为什么说 Follower 副本同步 Leader 的时候是一种异步操作呢?

Follower 副本在同步 Leader 副本后会把消息保存在本地 log 中,这个时候 Follower 会给 Leader 副本一个响应消息,告诉 Leader 自己已经保存成功了,同步复制的 Leader 会等待所有的 Follower 副本都写入成功后,再返回给 producer 写入成功的消息。而异步复制是 Leader 副本不需要关心 Follower 副本是否写入成功,只要 Leader 副本自己把消息保存到本地 log ,就会返回给 producer 写入成功的消息。

同步复制:

1.producer 通知 ZooKeeper 识别领导者;
2.producer 向领导者写入消息;
3.领导者收到消息后会把消息写入到本地 log;
4.跟随者会从领导者那里拉取消息;
5.跟随者向本地写入 log;
6.跟随者向领导者发送写入成功的消息;
7.领导者会收到所有的跟随者发送的消息;
8.领导者向 producer 发送写入成功的消息;

异步复制:

和同步复制的区别在于,领导者在写入本地log之后,直接向客户端发送写入成功消息,不需要等待所有跟随者复制完成。

3.5. ISR(In-Sync Replicas)

Kafka 动态维护了一个同步状态的副本的集合(a set of In-Sync Replicas),简称ISR。

ISR 也是一个很重要的概念,我们之前说过,追随者副本不提供服务,只是定期的异步拉取领导者副本的数据而已,拉取这个操作就相当于是复制,ctrl-c + ctrl-v大家肯定用的熟。那么是不是说 ISR 集合中的副本消息的数量都会与领导者副本消息数量一样呢?那也不一定,判断的依据是 broker 中参数 replica.lag.time.max.ms 的值,这个参数的含义就是跟随者副本能够落后领导者副本最长的时间间隔。

replica.lag.time.max.ms 参数默认的时间是 10秒,如果跟随者副本落后领导者副本的时间不超过 10秒,那么 Kafka 就认为领导者和跟随者是同步的。即使此时跟随者副本中存储的消息要小于领导者副本。如果跟随者副本要落后于领导者副本 10秒以上的话,跟随者副本就会从 ISR 被剔除。倘若该副本后面慢慢地追上了领导者的进度,那么它是能够重新被加回 ISR 的。这也表明,ISR 是一个动态调整的集合,而非静态不变的。

3.6. Unclean 领导者选举

既然 ISR 是可以动态调整的,那么必然会出现 ISR 集合中为空的情况,由于领导者副本是一定出现在 ISR 集合中的,那么 ISR 集合为空必然说明领导者副本也挂了,所以此时 Kafka 需要重新选举一个新的领导者,那么该如何选举呢?现在你需要转变一下思路,我们上面说 ISR 集合中一定是与领导者同步的副本,那么不再 ISR 集合中的副本一定是不与领导者同步的副本了,也就是不再 ISR 列表中的跟随者副本会丢失一些消息。

如果你开启 broker 端参数 unclean.leader.election.enable的话,下一个领导者就会在这些非同步的副本中选举。这种选举也叫做 Unclean 领导者选举。

如果你接触过分布式项目的话你一定知道 CAP 理论,那么这种 Unclean 领导者选举其实是牺牲了数据一致性,保证了 Kafka 的高可用性。你可以根据你的实际业务场景决定是否开启 Unclean 领导者选举,一般不建议开启这个参数,因为数据的一致性要比可用性重要的多。

4. Kafka 的请求处理流程

broker 的大部分工作是处理客户端、分区副本和控制器发送给分区领导者的请求。这种请求一般都是请求/响应式的,我猜测你接触最早的请求/响应的方式应该就是 HTTP 请求了

事实上,HTTP 请求可以是同步可以是异步的。一般正常的 HTTP 请求都是同步的,同步方式最大的一个特点是 提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能做任何事。

而异步方式最大的特点是 请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)-> 处理完毕。

注意: 我们只是使用 HTTP 请求来举例子,而 Kafka 采用的是 TCP 基于 Socket 的方式进行通讯。

以说同步请求就是顺序处理的,而异步请求的执行方式则不确定,因为异步需要创建多个执行线程,而每个线程的执行顺序不同。那么这两种方式有什么缺点呢?

  • 同步的方式最大的缺点就是吞吐量太差,资源利用率极低,由于只能顺序处理请求,因此,每个请求都必须等待前一个请求处理完毕才能得到处理。这种方式只适用于请求发送非常不频繁的系统。
  • 异步的方式的缺点就是 为每个请求都创建线程的做法开销极大,在某些场景下甚至会压垮整个服务。

4.1. 响应式模型

Kafka 采用同步还是异步的呢?都不是,Kafka 采用的是一种 响应式(Reactor)模型。

那么什么是响应式模型呢?简单的说,Reactor 模式是事件驱动架构的一种实现方式,特别适合应用于处理多个客户端并发向服务器端发送请求的场景,如下图所示:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
Kafka 的 broker 端有个 SocketServer组件,类似于处理器,SocketServer 是基于 TCP 的 Socket 连接的,它用于接受客户端请求,所有的请求消息都包含一个消息头,消息头中都包含如下信息:

  • Request type —(也就是 API Key)
  • Request version —(broker 可以处理不同版本的客户端请求,并根据客户版本做出不同的响应)
  • Correlation ID — 一个具有唯一性的数字,用于标示请求消息,同时也会出现在响应消息和错误日志中(用于诊断问题)
  • Client ID — 用于标示发送请求的客户端

broker 会在它所监听的每一个端口上运行一个 Acceptor 线程,这个线程会创建一个连接,并把它交给 Processor(网络线程池), Processor 的数量可以使用 num.network.threads 进行配置,其默认值是3,表示每台 broker 启动时会创建3个线程,专门处理客户端发送的请求。

Acceptor 线程会采用轮询的方式将入栈请求公平的发送至网络线程池中,因此,在实际使用过程中,这些线程通常具有相同的机率被分配到待处理请求队列中,然后从响应队列获取响应消息,把它们发送给客户端。Processor 网络线程池中的请求 - 响应的处理还是比较复杂的,下面是网络线程池中的处理流程图:
image
Processor 网络线程池接收到客户和其他 broker 发送来的消息后,网络线程池会把消息放到请求队列中,注意这个是共享请求队列,**因为网络线程池是多线程机制的,所以请求队列的消息是多线程共享的区域,**然后由 IO 线程池进行处理,根据消息的种类判断做何处理,比如 PRODUCE 请求,就会将消息写入到 log 日志中,如果是 FETCH 请求,则从磁盘或者页缓存中读取消息。也就是说,IO线程池是真正做判断,处理请求的一个组件。

在IO 线程池处理完毕后,就会判断是放入响应队列中还是 Purgatory 中,Purgatory 是什么我们下面再说,现在先说一下响应队列,响应队列是每个线程所独有的,因为响应式模型中不会关心请求发往何处,因此把响应回传的事情就交给每个线程了,所以也就不必共享了。

注意:IO 线程池可以通过 broker 端参数 num.io.threads 来配置,默认的线程数是8,表示每台 broker
启动后自动创建 8 个IO 处理线程。

4.2. 生产请求

producer 在向 kafka 写入消息的时候,怎么保证消息不丢失呢?那就是通过 ACK 应答机制!在生产者向队列写入数据的时候可以设置参数来确定是否确认 kafka 接收到数据,这个参数可设置的值为 0、1、all 。

简单来讲就是不同的配置对写入成功的界定是不同的,如果 acks = 1,那么只要领导者收到消息就表示写入成功,如果 acks = 0,表示只要领导者发送消息就表示写入成功,根本不用考虑返回值的影响。如果 acks = all,就表示领导者需要收到所有副本的消息后才表示写入成功。

在消息被写入分区的首领后,如果 acks 配置的值是 all,那么这些请求会被保存在 Purgatory 的缓冲区中,直到领导者副本发现跟随者副本都复制了消息,响应才会发送给客户端。

4.3. 获取请求

broker 获取请求的方式与处理生产请求的方式类似,客户端发送请求,向 broker 请求主题分区中特定偏移量的消息,如果偏移量存在,Kafka 会采用 零拷贝 技术向客户端发送消息,Kafka 会直接把消息从文件中发送到网络通道中,而不需要经过任何的缓冲区,从而获得更好的性能。

客户端可以设置获取请求数据的上限和下限,上限指的是客户端为接受足够消息分配的内存空间,这个限制比较重要,如果上限太大的话,很有可能直接耗尽客户端内存。下限可以理解为攒足了数据包再发送的意思,这样就增加了时间成本。如下图所示:
image
如图你可以看到,在 拉取消息 --> 消息 之间是有一个等待消息积累这么一个过程的,这个消息积累你可以把它想象成超时时间,不过超时会跑出异常,消息积累超时后会响应回执。延迟时间可以通过 replica.lag.time.max.ms 来配置,它指定了副本在复制消息时可被允许的最大延迟时间。

4.4. 元数据请求

生产请求和响应请求都必须发送给领导者副本,如果 broker 收到一个针对某个特定分区的请求,而该请求的首领在另外一个 broker 中,那么发送请求的客户端会收到 非分区首领 的错误响应;如果针对某个分区的请求被发送到不含有领导者的 broker 上,也会出现同样的错误。Kafka 客户端需要把请求和响应发送到正确的 broker 上。

事实上,**客户端会使用一种 元数据请求 ,这种请求会包含客户端感兴趣的主题列表,服务端的响应消息指明了主题的分区,领导者副本和跟随者副本。**元数据请求可以发送给任意一个 broker,因为所有的 broker 都会缓存这些信息。

一般情况下,客户端会把这些信息缓存,并直接向目标 broker 发送生产请求和相应请求,这些缓存需要隔一段时间就进行刷新,使用 metadata.max.age.ms 参数来配置,从而知道元数据是否发生了变更。比如,新的 broker 加入后,会触发重平衡,部分副本会移动到新的 broker 上。这时候,如果客户端收到 不是首领 的错误,客户端在发送请求之前刷新元数据缓存。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

最后

各位读者,由于本篇幅度过长,为了避免影响阅读体验,下面我就大概概括了整理了

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

GC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算**

  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值