乐趣区

关于SegmentFault:Kafka-探险-生产者源码分析-核心组件

这个 Kafka 的专题,我会从零碎整体架构,设计到代码落地。和大家一起杠源码,学技巧,涨常识。心愿大家继续关注一起见证成长!

我置信:技术的路线,十年如一日!十年磨一剑!

往期文章

Kafka 探险 – 架构简介

Kafka 探险 – 源码环境搭建

前言

咱们说 Kafka 是一个音讯队列,其实更加确切的说:是 Broker 这个核心部件。为何这么说?你会发现咱们能够通过控制台、Java 代码、C++ 代码、甚至是 Socket 向 Broker 写入音讯,只有咱们听从了 Kafka 写入音讯的协定,就能够将音讯发送到 Kafka 队列中。

用业余一点的话术来说,Kafka 定义了一个应用层的网络协议,只有咱们基于传输层结构出合乎这个协定的数据,就是非法的 Kafka 音讯。

所以说咱们写入 Kafka 音讯的只是一个生产者的客户端,他的模式多种多样,有 Java,Python,C++ 等多种实现,那么咱们每次发消息难道还须要本人去实现这套发送音讯的协定么?显然 Kafka 官网曾经思考到这个问题了,为了给咱们提供 开箱即用 的音讯队列,官网曾经帮咱们写好了各种语言的优质生产者实现,例如咱们明天要探讨的 Java 版本的实现。

思考

后面提到 Kafka 帮咱们实现了各个版本的生产者代码,其实他也能够齐全不提供这份代码,因为外围的队列的性能曾经实现了,这些客户端的代码也能够齐全交由用户本人实现。

那么如果没有官网代码,咱们又该实现一些什么性能,有哪些接口,哪些办法,以及如何组织这些代码呢。带着这样的问题咱们一起来思考一下!个别对于这种带有数据流转的设计,我会从 由谁产生? 什么数据?    通往哪去?  如何保障通路牢靠? 这几个方面来思考。

音讯天然是通过应用程序结构进去并提供给生产者,生产者首先要晓得须要将音讯发送到哪个 Broker 的哪个 Topic,以及 Topic 的具体 Partition。那么必然须要配置客户端的 Broker 集群地址 ,须要发送的 Topic 名称,以及 音讯的分区策略,是指定到具体的分区还是通过某个 key hash 到不同的分区。

晓得了音讯要通往哪,还须要晓得发送的是什么格局的音讯,是字符串还是数字或是被序列化的二进制对象。音讯序列化  将须要音讯序列化成字节数组才不便在网络上传输,所以要配置生产者的音讯序列化策略,最好是能够通过传递枚举或者类名的形式主动结构序列化器,便于后续序列化过程的扩大。

从下面一篇文章《Kafka 探险 – 架构简介》理解到:音讯队列经常用于多个零碎之间的异步调用,那么这种调用关系就没有强实时依赖。因为发消息到 Kafka 会产生 网络 I/O,相对来说比拟耗时,那么音讯发送这一动作除了同步调用,是否也能够设置为异步,进步生产者的吞吐呢?。并且大量音讯发送场景, 咱们能够设置一个窗口,窗口能够是工夫维度也能够是音讯数量维度,将音讯积攒起来批次发送,缩小网络 I/O 次数,进步吞吐量。

最初呢为了保障音讯能够最大水平的胜利发送到 Broker,咱们还须要一些 失败重试机制,例如失败后放到重试队列中,隔一段时间尝试再次发送。

理清思路

通过下面的剖析,咱们会有一个大抵的意识,应该会有哪些办法,以及底层的大抵的设计会分为哪几个局部。然而不够分明,不够清晰。

首先总结一下实现客户端的几个要点在于:

  1. 配置 Broker 根底信息:集群地址、Topic、Partition
  2. 音讯序列化,通过可扩大的序列化器实现
  3. 音讯异步写入缓冲区,网络 I/O 线程实现音讯发送
  4. 音讯发送的失败重试机制

话不多说,用一张图画出各个外围模块以及他们之间的交互程序:

用户设定 Kafka 集群信息,生产者从 Kafka Broker 上拉取 可用 Kafka 节点、Topic 以及 Partition 对应关系。缓存到生产者成员变量中,如果 Broker 集群有扩容,或者有机器下线须要从新获取这些服务信息。

客户端依据用户设置的序列化器,对音讯进行序列化,之后异步的将音讯写入到客户端缓冲区。缓冲区内的音讯达到肯定的数量或者达到一个工夫窗口后,网络 I/O 线程将音讯从缓冲区取走,发送到 Broker。

以上就是我对于一个 Kafka 生产者实现的思考,接下来看看官网的代码设计与咱们的思路有何差异,他又是为什么这么设计。

官网设计

其实通过下面的思考和整顿,咱们的设计曾经十分靠近 Kafka 的官网设计了,官网的模块拆分的更加粗疏,性能更加独立。

外围组件

首先看一眼 KafkaProducer 类中有哪些成员变量,这些变量就是 Producer 的外围组件。

其中外围字段的解释如下:

clinetId:标识发送者 Id

metric:统计指标

partitioner:分区器作用是决定音讯发到哪个分区。有 key 则依照 key 的 hash,否则应用 roundrobin

key/value Serializer:音讯 key/value 序列化器

interceptors:发送之前 / 后对音讯的对立解决

maxRequestSize:能够发送的最大音讯,默认值是 1M,即影响一个音讯 Record 的大小,此值在服务端也是有限度的。

maxBlockTimeMs:buffer 满了或者期待 metadata 信息的,超时的弥补机制

accumulator:累积缓冲器

networkClient:包装的网络层

sender:网络 I/O 线程

发送流程

发送一条音讯的时候,数据又是怎么在这些组件之间进行流转的呢?

Producer 调用 send 办法后,在从 Broker 获取的 Metadata 无效状况下,通过拦截器和序列化后,被分区器放到了一个缓冲区的特定地位,缓冲区由一个 ConcurrentHashMap 形成,key 为主题分区,value 是一个 deque 寄存音讯缓存块。从客户端角度来看如果无需关怀发送后果,发送流程就曾经完结了。

接下来是独立的 Sender 线程负责从缓冲中获取足量的数据调用 Network Client 封装层去真正发送数据,这里应用了 Java8 的 NIO 网络模型发送数据。

能够看到整个逻辑的关键点在于 RecordAccumulator 如何进行音讯缓存,个别的成熟框架和中间件中都会有一套本人的内存管理机制,比方 Netty 也有一套简单而又精妙的内存治理形象层,这里的缓冲区也是一样的情理,次要须要去看看 Kafka 如何去做内存治理。

另外须要关注 Sender 从缓冲里以什么样的逻辑获取数据,来达到尽量少的网络交互发送尽量多的数据。还有网络失败又是如何保证数据的可靠性的。这个中央也是咱们的设计和官网实现的差距,对于网络 I/O 的精心优化。

目前的篇幅曾经比拟长了,为了大家不便浏览了解,本篇次要从和大家一起思考如何设计一个 Kafka Producer 以及官网是如何实现的,咱们之间的差距是什么,更须要关注的点是什么。通过本人的思考和比照更加能意识到有余学习到新的点!

序幕(唠叨)

这篇文章从周内就开始了,前面断断续续每天写了点,只是每天回去的的确有点晚,偶然还给我整个失眠,精神状态不太好,周五六点多饭都没吃间接回家睡觉了,的确好困,心愿下周能劳动好。

这周的工作压力也很大,次要是须要推动很多上下游协同,还须要定计划。常常在想怎么交涉?怎么批改计划大家会认同?怎么压服他们?是压力也是锤炼,阐明这方面欠缺的较多,该补!

下篇文章次要会写 KafkaProducer 的缓存内存管理机制,Meta 信息更新机制,以及网络 I/O 模型的设计。敬请期待~

另外:大家也能够关注下我的微信公众号哦~ 技术分享和集体思考都会第一工夫同步!

退出移动版