31.生产者有哪些发消息的模式?
Kafka 生产者发送消息主要有三种模式:
发后即忘发送模式
发后即忘模式「fire-and-forget」,它只管发送消息,并不需要关心消息是否发送成功。其本质上也是一种异步发送的方式,消息先存储在缓冲区中,达到设定条件后再批量进行发送。这是 kafka 吞吐量最高的方式,但同时也是消息最不可靠的方式,因为对于发送失败的消息并没有做任何处理,某些异常情况下会导致消息丢失。
同步发送模式
同步发送模式 「sync」,调用 send() 方法会返回一个 Future 对象,再通过调用 Future 对象的 get() 方法,等待结果返回,根据返回的结果可以判断消息是否发送成功, 由于是同步发送会阻塞,只有当消息通过 get() 返回数据时,才会继续下一条消息的发送。
异步发送模式
异步发送模式「async」,在调用 send() 方法的时候指定一个 callback 函数,当 Broker 接收到返回的时候,该 callback 函数会被触发执行,通过回调函数能够对异常情况进行处理,当调用了回调函数时,只有回调函数执行完毕生产者才会结束,否则一直会阻塞。
以上三种方式各有各的特点,具体还要看业务的应用场景适合哪一种:
1)**场景1:**如果业务只是关心消息的吞吐量,且允许少量消息发送失败,也不关注消息的发送顺序的话,那么可以使用发后即忘发送「fire-and-forget」的方式,配合参数 acks = 0,这样生产者并不需要等待服务器的响应,以网络能支持的最大速度发送消息。
2)场景2:如果业务要求消息必须是按顺序发送的话,且数据只能落在一个 Partition 上,那么可以使用同步发送「sync」的方式,并结合参数来设置 retries 的值让消息发送失败时可以进行多次重试「retries > 0」,再结合参数设置「acks=all & max_in_flight_requests_per_connection=1」,可以控制生产者在收到服务器成功响应之前只能发送1条消息,在消息发送成功后立即进行 flush, 从而达到控制消息顺序发送。
3)**场景3:**如果业务需要知道消息是否发送成功,但对消息的顺序并不关心的话,那么可以用「异步async + 回调 callback 函数」的方式来发送消息,并配合参数 retries=0,待发送失败时将失败的消息记录到日志文件中进行后续处理。
32.Kafka 为什么要设计分区?
其实这个问题说来很简单, 假如不进行分区的话就如同 MySQL 单表存储一样,发消息就会被集中存储,这样会导致某台 Kafka 服务器存储 Topic 消息过多,如果在写消息压力很大的情况下,最终会导致这台 Kafka 服务器吞吐量出现瓶颈, 因此 Kafka 设计了分区的概念,同时也带来了「负载均衡」、「横向扩展」的能力,如下图所示:。
1)**负载均衡:**发送消息时可以根据分区的数量进行数据均匀分布,使其落在不同的分区上, 这样可以提高并发写性能;同时消费的时候多个订阅者可以从一个或者多个分区中同时消费数据,以支撑海量数据处理能力,提高读消息性能。
2)**横向扩展:**可以将一个 Topic 分成了多个 Partition,将不同的 Partition 尽可能的部署在不同的物理节点上,这样扩展起来非常方便,另外一个消费者可以消费多个分区中的数据,但是这样还是不能够充分的发挥横向扩展,这时候消费者组就出现了,我们用消费者组,来消费整个的 Topic,一个消费者消费 Topic 中的一个分区。
33.Kafka 如何合理设置分区数,越多越好吗?
一、Kafka 如何合理设置分区数
首先我们要了解在 Partition 级别上达到负载均衡是实现高吞吐量的关键,合适的 Partition 数量可以达到并行读写和负载均衡的目的,需要根据每个分区的生产者和消费者的目标吞吐量进行估计。
此时我们可以遵循一定的步骤来计算确定分区数:
1)首先根据某个 Topic 当前接收的数据量等经验来确定分区的初始值。
2)然后针对这个 Topic,进行测试 Producer 端吞吐量和 Consumer 端的吞吐量。
3)测试的结果, 假设此时他们的值分别是 Tp「Producer 端吞吐量」、Tc「负Consumer 端吞吐量」,总的目标吞吐量是 Tt, 单位是 MB/s, 那么结果 numPartition = Tt / max (Tp, Tc)。
4)**特殊说明:**测试 Tp 通常是很容易的,因为它的逻辑非常简单,就是直接发送消息到 Kafka 就好了。而测试 Tc 通常与应用消费消息后进行其他什么处理有关,相对复杂一些。
二、分区设置越多越好吗?
首先 Kafka 高吞吐量的原因之一就是通过 Partition 将 Topic 中的消息均衡保存到 Kafka 集群中不同的 Broker 中。
「理论上说,如果一个 Topic 分区越多,整个集群所能达到的吞吐量就越大」。但是,实际生产中 Kafka Topic 的分区数真的配置越多越好吗?很显然不是!分区数过多会有什么弊端和问题呢,我们可以从下面4个方向进行深度分析:
**
**
01
使用内存方面分析
1)**Broker端:**有很多组件都在内存中维护了分区级别的缓存,比如 Controller,FetcherManager 等,因此分区数越多,这类缓存的成本就越大。
2)**Producer端:**比如参数 batch.size,默认是16KB。它会为每个分区缓存消息,在数据积累到一定大小或者足够的时间时,累积的消息将会从缓存中移除并发往Broker 节点。这个功能是为了提高性能而设计,但是随着分区数增多,这部分缓存所需的内存占用也会更多。
3)**Consumer端:**消费者数跟分区数是直接挂钩的,在消费消息时的内存占用、以及为达到更高的吞吐性能需要开启的 Consumer 数也会随着分区数增加而增加。
02
消耗文件句柄方面分析
在 Kafka 的 Broker 中,每个 Partition 都会对应磁盘文件系统中一个目录。在 Kafka 的日志文件目录中,每个日志数据段都会分配三个文件,两个索引文件和一个数据文件。每个 Broker 会为每个日志段文件打开两个 index 文件句柄和一个 log 数据文件句柄。因此,随着 Partition 的增多,所需要保持打开状态的文件句柄数也就越多,最终可能超过底层操作系统配置的文件句柄数量限制。
**
03
端到端的延迟方面分析
首先我们得先了解 Kafka 端对端延迟是什么? Producer 端发布消息到 Consumer 端接收消息所需要的时间,即 Consumer 端接收消息的时间减去 Producer 端发布消息的时间。
在 Kafka 中只对「已提交的消息做最大限度的持久化保证不丢失」,因此 Kafka 只有在消息提交之后,才会将消息暴露给消费者。此时如果分区越多那么副本之间需要同步的数据就会越多,假如消息需要在所有 ISR 副本集合列表同步复制完成之后才能进行暴露。因此 ISR 副本集合间复制数据所花时间将是 kafka 端对端延迟的最主要部分。
此时我们可以通过加大 kafka 集群来进行缓解。比如,我们将 100 个分区 Leader 放到一个 Broker 节点和放到 10 个 Broker 节点,它们之间的延迟是不一样的。如在 10 个 Broker 节点的集群中,每个 Broker 节点平均只需要处理 10 个分区的数据复制。此时端对端的延迟将会变成原来的十分之一。
**因此根据实战经验,如果你特别关心消息延迟问题的话,此时限制每个 Broker 节点的 Partition 数量是一个非常不错的主意:**对于 N 个 Broker 节点和复制副本因子「replication-factor」为 F 的 Kafka 集群,那么整个 Kafka 集群的 Partition 数量最好不超过 「100 * N * F」 个,即单个 Broker 节点 Partition 的 Leader 数量不超过100。
**
04
高可用性方面分析
**
我们知道 Kafka 是通过多副本复制技术来实现集群的高可用和稳定性的。每个 Partition 都会有多个数据副本,每个副本分别存在于不同的 Broker 上。所有的数据副本中,其中一个数据副本为 Leader,其他的数据副本为 Follower。
**在Kafka集群内部,所有的数据副本采用自动化的方式管理且会确保所有副本之间的数据是保持同步状态的。**当 Broker 发生故障时,对于 Leader 副本所在 Broker 的所有 Partition 将会变得暂不可用。Kafka 将自动在其它副本中选择出一个 Leader,用于接收客户端的请求。这个过程由 Kafka Controller 节点 Broker 自动选举完成。
正常情况下,当一个 Broker 在有计划地停止服务时候,那么 Controller 会在服务停止之前,将该 Broker上 的所有 Leader 副本一个个地移走。对于单个 Leader 副本的移动速度非常快,从客户层面看,有计划的服务停服只会导致系统很短时间窗口不可用。
但是,当 Broker 不是正常停止服务时「kill -9 强杀方式」,系统的不可用时间窗口将会与受影响的 Partition 数量有关。如果此时发生宕机的 Broker 是 Controller 节点时, 这时 Controller 节点故障恢复会自动的进行,但是新的 Controller 节点需要从 Zookeeper 中读取每一个 Partition 的元数据信息用于初始化数据。假设一个 Kafka 集群存在10000个 Partition,从 Zookeeper 中恢复元数据时每个 Partition 大约花费2ms,则 Controller 恢复将会增加约20秒的不可用时间窗口。
**
总之,通常情况下 Kafka 集群中越多的 Partition 会带来越高的吞吐量。但是,如果 Kafka 集群中 Partition 总量过大或者单个 Broker 节点 Partition 过多,都可能会对系统的可用性和消息延迟带来潜在的负面影响,需要引起我们的重视。
34.Kafka 副本有哪两种,作用是什么?
在 Kafka 中,为实现「数据备份」的功能,保证集群中的某个节点发生故障时,该节点上的 Partition 数据不丢失,且 Kafka 仍然能够继续工作,为此 Kafka 提供了副本机制,一个 Topic 的每个 Partition 都有若干个副本,一个 Leader 副本和若干个 Follower 副本。
1)Leader 主副本负责对外提供读写数据服务。
2)Follower 从副本只负责和 Leader 副本保持数据同步,并不对外提供任何服务。
35.Kafka 读写数据这么快是如何做到的?
1.Kafka本身是分布式集群,可以采用分区技术,并行度高
2.Kafka读数据采用稀疏索引,可以快速定位要消费的数据
3.顺序写磁盘:Kafka的producer生产数据,要写入到log文件中,写的过程是一直追加到文件末端,为顺序写。
4.PageCache页缓存:Kafka重度依赖底层操作系统提供的PageCache功能。当上层有写操作时,操作系统只是将数据写入PageCache。当读操作发生时,先从PageCache中查找,如果找不到,再去磁盘中读取。实际上PageCache是把尽可能多的空闲内存都当做了磁盘缓存来使用。
5.零拷贝技术
**
kafka 为了解决内核态和用户态数据不必要 Copy 这个问题, 在读取数据的时候就引入了「零拷贝技术」。即让操作系统的 os cache 中的数据直接发送到网卡后传出给下游的消费者,中间跳过了两次拷贝数据的步骤,从而减少拷贝的 CPU 开销, 减少用户态内核态的上下文切换次数, 从而优化数据传输的性能, 而Socket缓存中仅仅会拷贝一个描述符过去,不会拷贝数据到Socket缓存,如下图所示:
在 Kafka 中主要有以下两个地方使用到了「零拷贝技术」:
1)基于 mmap 机制实现的索引文件:首先索引文件都是基于 MappedByBuffer 实现,即让用户态和内核态来共享内核态的数据缓冲区,此时数据不需要 Copy 到用户态空间。虽然 mmap 避免了不必要的 Copy,但是在不同操作系统下, 其创建和销毁成功是不一样的,不一定都能保证高性能。所以在 Kafka 中只有索引文件使用了 mmap。
2)**基于sendfile 机制实现的日志文件读写:**在 Kafka 传输层接口中有个 TransportLayer 接口,它的实现类中有使用了 Java FileChannel 中 transferTo 方法。该方法底层就是使用 sendfile 实现的零拷贝机制, 目前只是在 I/O 通道是普通的 PLAINTEXT 的时候才会使用到零拷贝机制。
04
6.消息批量发送
Kafka 在发送消息的时候并不是一条条的发送的,而是会把多条消息合并成一个批次Batch 进行处理发送,消费消息也是同样,一次拉取一批次的消息进行消费。如下图所示:
05
7.数据压缩
在 Kafka 中三个端都使用了优化后的压缩算法,压缩有助于提高吞吐量, 降低延迟并提高磁盘利用率。Kafka 底层支持多种压缩算法: lz4, snappy, gzip**, 从Kafka 2.1.0 开始新增了 ZStandard 算法, 该算法是 Facebook 开源的压缩算法, 能提供超高的压缩比。
在 Kafka 中, 压缩可能会发生在两个地方: 生产者端和Broker端。一句话总结下压缩和解压缩, 即 Producer 端压缩, Broker 端保持, Consumer 端解压缩,这样可以节省大量的网络和磁盘开销。
36.数据一致性(木桶原理)
kafka是通过HW(High Water Mark) 机制来保证数据的一致性。
(1)follower故障
follower发生故障后会被临时踢出ISR,待该follower恢复后,follower会读取本地磁盘记录的上次的HW,并将log文件高于HW的部分截取掉,从HW开始向leader进行同步。等该follower的LEO大于等于该Partition的HW,即follower追上leader之后,就可以重新加入ISR了。
(2)leader故障
leader发生故障之后,会从ISR中选出一个新的leader,之后,为保证多个副本之间的数据一致性,其余的follower会先将各自的log文件高于HW的部分截掉,然后从新的leader同步数据。
37.Kafka中消息可以被多个消费者消费吗?
Kafka 是一种分布式发布订阅消息系统,它可以让多个消费者同时消费一个 topic。Kafka的消费者模型是一种分布式消费者模型,它允许多个消费者同时消费一个 topic,从而提高消息的消费效率。
Kafka 的消费者模型是基于分区的,每个消费者只能消费一个分区,而一个 topic 可以有多个分区,因此多个消费者可以同时消费一个 topic。每个消费者都有自己的消费者组,每个消费者组可以有多个消费者,每个消费者组只能消费一个 topic,但是每个消费者组可以有多个消费者,因此多个消费者可以同时消费一个 topic。
Kafka 的消费者模型可以提高消息的消费效率,因为多个消费者可以同时消费一个 topic从而提高消息的消费速度。此外,Kafka 的消费者模型还可以提高消息的可靠性,因为多个消费者可以同时消费一个 topic,从而减少消息的丢失
Kafka 的消费者模型可以提高消息的消费效率和可靠性,因此它是一种非常有效的消息消费模型。它可以让多个消费者同时消费一个 topic,从而提高消息的消费效率和可靠性.使得消息的传输更加高效。
38.Kafka会在哪些场景出现重复消费,为什么
导致Kafka消息重复消费有以下两个原因:
1.Kafka消息重复提交导致消息重复消费
默认情况下,消息消费完后,会自动提交Offset的值,避免重复消费。Kafka消费端的自动提交,会有一个默认5秒的间隔,在5秒之后的下一次向Broker拉取消息的时候才提交上一批消费的offset。如果在消费过程中,如果遇到应用程序被强制kill掉,就会导致Offset没有及时提交,导致消息重复消费。(消费者宕机造成重复消费)
2.Kafka服务端的Partition在均衡机制导致消息重复消费。
在Kafka服务端的Partition再均衡机制导致消息重复消费,会把多个Partition均衡的分配给多个消费者,消费者会从分配的partition里面去消费消息,如果消费者在默认的5分钟内没有去处理完这批消息的话就会触发kafka的reblance机制。从而导致offset自动提交失败。而Rebalance之后,消费者还会从之前没有提交的offset位置开始消费,从而导致消息重复消费。(1.session超时造成重复消费 2.处理能力弱造成重复消费)
解决重复消费消息的问题有以下方法:
1.提高消费端处理性能避免触发Balance
用多线程的方式处理消息,缩短单个消息消费的时长。
调整消息处理的超时时间
减少一次性从Broker上拉取数据的条数。
2.使用ConsumerRebalanceListener,再均衡监听器
用来设定发生再均衡动作前后的一些准备或者收尾工作。
3.使用消息幂等性
将消息生成的唯一id保存到mysql或者redis中,再处理消息之前先查mysql或者redis,判断是否已经消费过。
39.谈谈你对Kafka Leader选举机制是如何理解?
这里所说的 Leader 选举是指分区 Leader 副本的选举,它是由 Kafka Controller 负责具体执行的,当创建分区或分区副本上线的时候都需要执行 Leader 的选举动作。
有以下场景可能会触发选举:
1)当 Controller 感知到分区 Leader 下线需要执行 Leader 选举。
此时的选举策略是:Controller 会从 AR 副本集合(同时也在ISR 副本集合)中按照副本的顺序取出第一个存活的副本作为 Leader。
⼀个分区的 AR 副本集合在分配的时候就被指定,并且只要不发⽣重分配集合内部副本的顺序是保持不变的,而分区的 ISR 副本集合中副本的顺序可能会改变。
注意这里是根据 AR 副本集合的顺序而不是 ISR 副本结合的顺序进行选举的。
此时如果 ISR 副本集合中没有可用的副本,还需要再检查⼀下所配置的 unclean.leader.election.enable 参数「** 默认值为false**」。如果这个参数配置为true,那么表示允许从非 ISR 副本集合中选举 Leader,从 AR 副本集合列表中找到第⼀个存活的副本即为 Leader。
2)当分区进行重分配的时候也需要进行 Leader 选举。
此时的选举策略是:从重分配的 AR 副本集合中找到第⼀个存活的副本,且这个副本在当前的 ISR 副本集合中。当发生优先副本的选举时,直接将优先副本设置为 Leader 即可,AR 副本集合中的第⼀个副本即为优先副本。
3)当某节点执⾏ ControlledShutdown 被优雅地关闭时,位于这个节点上的 Leader 副本都会下线,所以与此对应的分区需要执行 Leader 的选举。
此时的选举策略是:从 AR 副本集合中找到第⼀个存活的副本,且这个副本在当前的 ISR 副本集合中,同时还要确保这个副本不处于正在被关闭的节点上。
40.Kafka发送消息方式
生产者发送给Kafka数据,可以采用同步方式和异步方式
同步方式:
发送一批数据给Kafka后,等待Kafka返回结果:
1.生产者等待10s,如果broker没有给ack响应,就认为失败
2.生产者重试3次,如果还没有响应,就报错
异步方式:
发送一批数据给Kafka,只是提供一个回调函数:
1.先将数据保存在生产者端的buffer中,buffer大小是2万条
2.满足数据阈值会数量阈值其中的一个条件就可以发送数
3.发送一批数据的大小是500条
注:如果broker迟迟不给ack,而buffer又满了,开发者可以设置是否直接清空buffer中的数据
41.Kafka如何提升吞吐量?
1)提升生产吞吐量
(1)buffer.memory:发送消息的缓冲区大小,默认值是32m,可以增加到64m。
(2)batch.size:默认是16k。如果batch设置太小,会导致频繁网络请求,吞吐量下降;如果batch太大,会导致一条消息需要等待很久才能被发送出去,增加网络延时。
(3)linger.ms,这个值默认是0,意思就是消息必须立即被发送。一般设置一个5-100毫秒。如果linger.ms设置的太小,会导致频繁网络请求,吞吐量下降;如果linger.ms太长,会导致一条消息需要等待很久才能被发送出去,增加网络延时。
(4)compression.type:默认是none,不压缩,但是也可以使用lz4压缩,效率还是不错的,压缩之后可以减小数据量,提升吞吐量,但是会加大producer端的CPU开销。
2)增加分区
3)消费者提高吞吐量
(1)调整fetch.max.bytes大小,默认是50m。
(2)调整max.poll.records大小,默认是500条。
4)增加下游消费者处理能力