参考链接:
https://blog.csdn.net/LeoHan163/article/details/105902707
https://developer.aliyun.com/article/763602
1、生产者工作流程
在生产者将消息发送出去之前,需要经历拦截器(Interceptor)、序列化器(Serializer)和分区器(Partitioner)等一系列的作用,随后才真正进入发送消息发送流程。
整个生产者客户端由两个线程协调运行,这两个线程分别为主线程和Sender线程(发送线程)。在主线程中由KafkaProducer创建消息,然后通过可能的拦截器、序列化器和分区器的作用之后缓存到消息累加器(RecordAccumulator,也称为消息收集器)中。Sender线程负责从RecordAccumulator中获取消息并将其发送到Kafka中。
根据上图,我们可以将生产者工作流程分为以下几步:
步骤一:一条消息过来首先会被封装成为一个 ProducerRecord 对象。
步骤二:接下来要对这个对象进行序列化,因为 Kafka 的消息需要从客户端传到服务端,涉及到网络传输,所以需要实现序列。Kafka 提供了默认的序列化机制,也支持自定义序列化。
步骤三:消息序列化完了以后,对消息要进行分区,分区的时候需要获取集群的元数据。分区的这个过程很关键,因为这个时候就决定了,我们的这条消息会被发送到 Kafka 服务端到哪个主题的哪个分区了。
步骤四:分好区的消息不是直接被发送到服务端,而是放入了生产者的一个缓存里面。在这个缓存里面,默认一个批次的大小是 16K。
步骤五:Sender 线程启动以后会从缓存里面去获取可以发送的批次。
步骤六:Sender 线程把一个一个批次发送到服务端。大家要注意这个设计,在 Kafka0.8 版本以前,Kafka 生产者的设计是来一条数据,就往服务端发送一条数据,频繁的发生网络请求,结果性能很差。后面的版本再次架构演进的时候把这儿改成了批处理的方式,性能指数级的提升。
2、生产者分区
根据之前的描述,我们知道生产者得到的消息最终是以分区为分类的,那么为什么要使用分区,以及使用分区有什么好处呢?
①使用分区的原因
(1)方便在集群中扩展,每个 Partition 可以通过调整以适应它所在的机器,而一个 topic又可以由多个 Partition 组成,可以很好的将很大的数据分散到集群的各个机器中;
(2)可以提高并发,因为可以以 Partition 为单位读写了,在之前我们聊到过,消费者其实是以分区为消费对象的,因为如果一个消费者组消费一个topic(主题),没有必要消费者组中每个消费者都要得到所有消息,只需要这个消费者组得到就行,那么如何将消息平均发送给消费者组中的消费者呢?就是利用分区。
②分区分配策略
一个 consumer group(消费者组) 中有多个 consumer,一个 topic(主题) 有多个 partition,所以必然会涉及到 partition 的分配问题,即确定那个 partition 由哪个 consumer 来消费。Kafka 有两种分配策略,一是 RoundRobin,一是 Range。
(1)RoundRobin分区分配策略
将每个topic中的分区进行排序,然后按照轮询的方式给每个consumer分配分区
注:是每个topic都按上述过程分配一遍,而不是所有topic加起来排序再分配
(1)Range分区分配策略
如图我们可以发现,range分配是按一个范围一个范围的分,即先将所有分区数除以consumer个数,然后按范围分配
③如何判断一个消息要传到那个分区中
首先我们知道一个消息传到生产者处时会封装成 ProducerRecord 对象。
下面我们根据api中相应的函数来说明一下kafka是如何判断一个消息是要传到那个分区中的:
(1)指明 partition