文章内容选自《Kafka技术内幕图解》
1.消息系统通常是由三大块组成:生产者、消费者、消息代理。
功能:生产者会将消息写入消息代理中,消费者会从消息代理中读取消息。对于消息代理而言,消费者和生产者都是客户端。
2.通信步骤:
1.生产者客户端应用产生消息。
2.生产者包装消息到请求头中,发送到客户端。
3.服务端对象负责接收请求,并将消息以文件形式存储。
4.服务端将响应结果返回给客户端。
5.消费者开始消费消息。
6.消费者将请求消息封装到请求中,发送给服务端。
7.服务端从文件系统中取出消息。
8.服务端返回响应结果给消费者客户端。
9.客户端将响应结果还原成消息,并处理消息。
3.网络通信协议:协议是在服务端定制的。
4.客户端采用缓存消息队列的模型来做成批量的。服务端为了加速响应做成了Selector模型。
5.分析抽象的东西:【核心思想:万物皆是对象 ->升华到万物皆可抽象 ->最后万物皆可盘【皆可设计】】
生产者产生的记录,形式是多样的,太抽象了。设计出来了ProducerRecord类。
生产者多样化也可以抽象,抽象出KafkaProducer生产者类。
6.!!!生产者的发送流程,首先序列化消息的key和value,然后为每条消息选择对应的分区(表示将消息存储到Kafka集群的哪一个节点上),最后通知发送线程发送消息【同步和异步都有,构造函数可以设置】。
7.Kafka一个主题有多个分区,消息是并行任务的最小单位,为消息选择分区要根据消息是否有键来判断。通常消息是没有键值的,为了让消息分散到不同的节点上。没有键值的方式消息,会均匀的分发到不同的分区。
8.有分区键的消息选择分区算法:对键值进行散列化,再于分区数量进行取模运算的到分区编号。对一个不变只的键进行散列化是不变的,总会落到同一个分区里面
9.每个分区如何发送到对应的节点上呢?
PartitionInfo记录了分区的基本信息:主题名称、分区编号、分区的主副本节点,分区的所有副本、分区中处于ISR的副本。
消息集的每一条消息都会选择一个分区编号。不同分区可以同时向分区的主副节点发送请求。生产者客户端采用这种分区并行发送的,从而提升生产者客户端的写入性能。分区对消费者也可以好处。他可以同时从多个分区读取消息。
10,有键值和没有键值的算法?有键值的算法:散列值%分区数量。有键值的算法,递增计数器%分区大小
11.键值选择分区还有一个重要的目的就是决定了消息发送到哪一个节点。
12.生产者发送的消息先在客户端缓存到消息记录器RecordAccumulator中,等到一定时机再有发送线程Sender批量写入Kafka集群。生产者每生产一条消息,就会向记录器中追加一条消息,追加方法的返回值表示批记录是否满了,如果批记录满了,则开始发送这一批记录。
消息产生->获取分区号->得到分区所属队列->然后取出队列中最后一个批记录->如果不存在批记录或者上一个批记录被填满,应该创建批记录,并加在队列尾部->每个记录收集器会按照分区进行分组,并放到batches集合中。
batches集合我们可以理解为一个表。每个分区可以理解为一个文件夹,每个分区中的批记录可以理解为文件夹下一个个小文件。
13.消息发送线程有两种消息发送方式:按照分区直接发送、按照分区的目标节点发送。我们再对消息的分区进行归类,减少请求的数量。
14,从记录收集器数据结构!!!!采用从顶向下的结构,上层TopicPartition->NodeId,TopicPartition->RecordBatch.
15.消息发送架构图:
16.发送线程并不负责真正的发送客户端请求,它会从记录收集器中取出要发送的消息,创建好客户端请求,并把请求交给客户端网络对象NetworkClient去发送。因为没有在发送线程中发送请求,所以创建客户端请求时需要保留目标节点,这样客户端网络对象取出客户端请求时
17.问题1.为什么要有记录收集器?作用是什么?因为分区的发送,可能存在多个分区发送到一个节点的情况,记录器可以做一个整合。
问题2:为什么要有客户端请求?发送线程发送有什么问题?客户端请求是一个职责上的抽象划分,如果没有客户端请求而是直接写到发送线程中。从分区到字典的数据结构就会有很大的问题,不易管理,因为网络发送是字节缓冲区。
18.客户端网络请求的限制:针对同一个服务端,如果上一个客户端请求还没有完成,则不允许发送新的客户端请求。