你绝对能看懂的Kafka源代码分析-Kafka Producer设计分析

之前我写了《Kafka入门教程轻松学》系列文章,半年来有1万多的阅读量,虽然不算很多,但看到很多朋友的支持,也给了我继续写下去的动力。接下来我会再写一个系列文章----《你绝对能看懂的Kafka源代码分析》,对Kafka源代码进行分析。文章还是延续之前的特色,采取形象的例子和图表帮助阅读者理解。

源代码阅读是一个难度很大,也比较枯燥的工作。所以这个系列文章持续时间会比较长,我有时间就会写一点。

目录:

《Kafka Producer设计分析》

《KafkaProducer类代码分析》

《RecordAccumulator类代码分析》

《Sender类代码分析》

《NetworkClient类代码分析》

------------------------------------------------

 

环境准备

在阅读Kafka源代码之前,需要做环境准备,我使用的是intelliJ IDEA,我们按照如下步骤操作:

1、下载Kafka源代码包https://www.apache.org/dyn/closer.cgi?path=/kafka/2.1.1/kafka-2.1.1-src.tgz

2、源代码根目录下运行gradle idea

3、启动IDEA,导入项目

现在你就在IDEA中查看Kafka的源代码了。

 

Producer源代码分析

 

Producer设计分析

客户端通过调用producer进行消息发送,这是消息的起源,所以我们最先分析Producer源代码。但如果一上来就切入代码,难免晦涩难以理解,在这里我先以生活中的例子作为开始,帮助理解Producer的设计理念。

我们举一个很常见的例子:发快递。为什么举这个例子呢,因为Kafka采用NIO通讯,如果大家学习过NIO,会知道经常用发快递来举例讲解NIO。另外发快递的场景确实也很像Kafka producer发送消息的场景。

快递公司一般是这样运作,由快递员上门取件,统一送回站点,站点进行分拣,同一个地区的包裹会装进一辆车,装满后发送出去。

在这个过程中,有如下几个角色

  1. 收件员,负责把快递运送回站点
  2. 分拣员,负责把快递按地区分类,比如唐山和秦皇岛,虽然具体的地址不一样但是都属于一个大区。
  3. 运输车,负责把快递运输出去,送到指定地区。

我们思考一下快递公司这样设计的好处

  1. 角色分工,收件员只负责收取快递,黏贴快递单,协助打包,然后送回站点。分拣员负责分类。运输车负责运输。分工明确,相互之间不需要知道对方在做什么。多人并行工作。
  2. 快递累积,分拣员把快递按地区分类,够一车后,发送出去。而不是收到一个件就发送出去。这是快递公司的通常做法,显然效率是更高的。然而实效性要求更高的闪送,则是收到一个件,马上发送出去,这样做是延迟最小的方式。
  3. 同区归并,真正发送的时候,唐山的快递和秦皇岛的快递会在一辆车发送出去,先统一发往河北总站。到达总站后再分车运往具体的城市。这样减少了发车的频次。如果唐山一车,秦皇岛一车,如果装不满一车,就会造成资源浪费,并且发车次数变多。

了解了快递公司的工作方式后,我们在宏观上看一下KafkaProducer的设计。

KafkaProducer的设计理念如出一辙,首先主线程把要发送的消息按照主题分区进行累积,达到一定数量后,触发发送线程进行发送。为了提高发送的效率,把发往同一个服务器的消息进行归并,一次性发往相应的服务器。

Producer设计中也有相应的角色:

  1. 收件员-KafkaProducer。实际除收件员外,它还承担了更多的工作。我们发送消息第一步就是调用KafkaProducer.send()方法
  2. 分拣员-RecordAccumulator。负责把消息按照分区分类,放入相应队列的ProducerBatch
  3. 运输车-Sender。负责运输,把消息真正发送出去。其实它内部还很复杂,通过NIO实现网络传输。

此外还有些相关类如下:

  1. ProducerBatch,每个ProducerBatch是一个信件箱,而同一个patition的信件箱码放在一起,程序中这就是Deque<ProducerBatch>
  2. ClientRequest,可以理解为运输车的货箱,在运输前,我们会把发往同一个服务器的消息放入ClientRequest,那么只需要发送一次ClientRequest,就可以把不同主题不同分区,但发往同一台服务器上的消息,一次性发送过去。

通过以上的讲解,Producer涉及到的主要类都已经进行了简单讲解,各自负责的事情也很清晰。下面我给出一张图,通过此图来讲解producer工作的主流程:

图中可以看到,有两个线程同时在工作,一个线程负责把消息送往消息站进行分组,另外一个线程负责把消息真正发送出去。

客户端发送消息时调用KafkaProducer的send方法。内部逻辑如下:

  1. 首先对消息进行加工,如序列化,选择分区等。
  2. 然后通过RecordAccumulator把加工好的消息放入相应的ProducerBatch中。
  3. 当batch满时,触发sender线程工作
  4. sender线程首先把batche从原列表中取出来,按照发往broker进行分组,然后封装到ClientRequest中
  5. 最后通过NIO的方式把ClientRequest发往相应的broker

至此,我们应该已经理解了Kafka Producer的设计思路。可见所有软件设计都来源于生活,都是对生活中的相应场景进行抽象和面向对象的设计。软件是无形的,但是实际生活中我们所采用的工作方式是有型的。通过参照实际生活场景和解决方案做软件的设计,让你的设计贴近实际场景,这样的代码写出来易于理解和扩展。

下一小节,我们将会深入每个方法中,分析在发送过程中发生了什么。

下一节《KafkaProducer类代码分析》


版权声明:本文为liyiming2017原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
THE END
< <上一篇
下一篇>>