了解Kafka生产者
了解Kafka生产者
之前对kafka的整体架构有浅显的了解,这次正好有时间,准备深入了解一下kafka,首先先从数据的生产者开始吧。
生产者的整体架构

可以看到整个生产者进程主要由两个线程进行协调工作,其中一个是主线程,首先由KafkaProducer创建消息,然后通过拦截器、消息序列化器、分区器的处理后,缓存到消息累加器中。另一个是Sender线程,负责从消息累加器中获取消息,并发送至Kafka集群中。
下面来具体分析各个组件的作用,以便加深了解。
拦截器: 从名字就可看出是按照一定规则对消息进行过滤。这个具体的规则可以自己去重写kafka中的ProducerInterceptorPrefix类中的onSend方法来实现。之后在KafkaProducer的配置参数 interceptor.classes中指定该拦截器来进行使用。还可以指定多个拦截器,组成拦截链。
序列化器:生产者需要使用它将消息对象转化为字节数组发送给kafka集群。消费者端进行反序列化还原消息对象。kafka中自带序列化器StringSerializer可对String、VyteArray、ByteBuffer等等类型进行序列化。kafka支持自定义序列化器,实现Serializer,重写serialize方法,即可实现自定义序列化器。修改配置文件中的value.serializer参数为自定义的类名即可。
分区器:顾名思义,分区器就是控制消息最终发往那个分区。若ProducerRecord中指定了partition字段,将发往指定的分区。反之,将根据消息的key来计算partition的值,拥有相同key的消息会被写入同一分区,key为null的消息,将会以轮询的方式发往topic内的各个可用分区。topic中的分区数量不变时,key与分区之间的映射关系保持不变。当topic内的分区数量变化时,该关系将难以保持。
注意:
如果key为null,计算得到的分区号仅为可用分区中的任意一个。当key不为null时,计算得到的分区号是所有分区中的任意一个。
当然,分区器也可以自定义,实现Partitioner中的partition方法即可。同样也是通过配置参数partitioner.class来显式指定这个分区器。
消息累加器:RecordAccumulator主要用来缓存消息,使Sender线程可以批量发送消息,进而减少网络传输的前期准备和收尾的资源消耗,提升性能和效率。缓存大小可以通过buffer.memory配置,默认值32MB。主程序生产的消息(ProducerRecord)都会追加到ProducerBatch尾部。消息累加器里面维护了一个Deque,他们之间的关系是多个ProducerRecord组成了一个ProducerBatch,多个ProducerBatch组成了一个Deque,这样的结构同样也是为了使消息排列更紧凑,提升效率和性能。
消息在生产端的生命历程
在RecordAccumulator内部还有一个BufferPool,用来实现ByteBuffer的复用,以实现缓存的高效利用,不过BufferPool只针对特定大小的ByteBuffer,其他大小的ByteBuffer不会缓存进BufferPool,这个特定大小由batch.size来指定,默认值为16KB。
当消息进入RecordAccumulator时,会先寻找指定分区所对应的Deque(若无将创建),再从Deque获取尾部的PoducerBatch(若无将创建),查看是否还能写入该消息(ProducerRecord),若可以则写入,否则将创建一个新的ProducerRecord,在创建时评估消息大小是否超过batch.size的大小,若不超过,PoducerBatch大小为该值,超过的话,将以消息大小创建PoducerBatch。换句话说,该PoducerBatch中仅有一条消息。
以batch.size创建的PoducerBatch可以通过BufferPool的管理来进行复用。
Sender从RecordAccumulator中获取缓存的消息之后,会将消息格式<分区,Deque>转换为<Node,List>,Node为kafka集群中的broker节点。之后Sender还会进一步封装成<Node,Request>的形式,这样就可以将Request请求发往各个Node了。请求在从Sender线程发往Kafka之前还会保存到InFlightRequests中,消息格式为Map<NodeId,Deque>,主要作用是缓存了已经发出去但还没有收到响应的请求。InFlightRequests可以通过配置参数来限制每个连接最多缓存的请求数。配置参数为max.in.flight.requests.per.connection,默认值为5。类似于golang中的channel,通道中的未响应请求数量达到5个,将阻塞,当被缓存的未响应请求收到响应,可以继续添加。
生产端的元数据更新
kafka中的元数据:集群中的topic信息,每个topic有哪些分区,每个分区的leader副本分配在哪个节点上,follower副本分配在哪些节点上,具体哪些副本在AR、ISR等集合中,集群中有哪些节点,控制器节点是哪个等信息。
当生产者客户端发送的消息中缺失需要的元数据时(例如未指定topic),或超过metadata.max.age.ms 时间没有更新元数据都会引起元数据的更新操作。默认值为5分钟。元数据的更新是在客户端内部进行的,对外部使用者不可见,也就是开发者不可感知到元数据的更新。当需要更新元数据时,会先挑选出lwastLoaderNode,然后向这个Node发送MetadataRequest请求来获取具体的元数据信息,该操作由Sender发起,在创建完MetadayaRequest之后,会存入InflightRequests,之后的步骤和发送消息类似,元数据的更新虽然由Sender线程负责更新,但是主线程也需要读取这些信息,这里的数据同步通过synchronized和final关键字来保障。
今天也是了解了大概的原理,每个细节都值得去深挖,故写文自勉。
了解Kafka生产者的更多相关文章
- 【转】 详解Kafka生产者Producer配置
粘贴一下这个配置,与我自己的程序做对比,看看能不能完善我的异步带代码: ----------------------------------------- 详解Kafka生产者Produce ...
- Kafka生产者-向Kafka中写入数据
(1)生产者概览 (1)不同的应用场景对消息有不同的需求,即是否允许消息丢失.重复.延迟以及吞吐量的要求.不同场景对Kafka生产者的API使用和配置会有直接的影响. 例子1:信用卡事务处理系统,不允 ...
- Python 使用python-kafka类库开发kafka生产者&消费者&客户端
使用python-kafka类库开发kafka生产者&消费者&客户端 By: 授客 QQ:1033553122 1.测试环境 python 3.4 zookeeper- ...
- Kafka集群安装部署、Kafka生产者、Kafka消费者
Storm上游数据源之Kakfa 目标: 理解Storm消费的数据来源.理解JMS规范.理解Kafka核心组件.掌握Kakfa生产者API.掌握Kafka消费者API.对流式计算的生态环境有深入的了解 ...
- [Spark][kafka]kafka 生产者,消费者 互动例子
[Spark][kafka]kafka 生产者,消费者 互动例子 # pwd/usr/local/kafka_2.11-0.10.0.1/bin 创建topic:# ./kafka-topics.sh ...
- Kafka权威指南 读书笔记之(三)Kafka 生产者一一向 Kafka 写入数据
不管是把 Kafka 作为消息队列.消息总线还是数据存储平台来使用 ,总是需要有一个可以往 Kafka 写入数据的生产者和一个从 Kafka 读取数据的消费者,或者一个兼具两种角色的应用程序. 开发者 ...
- kafka生产者
1.kafka生产者是线程安全的,她允许多个线程共享一个kafka实例 2.kafka管理一个简单的后台线程,所有的IO操作以及与每个broker的tcp连接通信,如果没有正确的关闭生产者可能会造成资 ...
- java实现Kafka生产者示例
使用java实现Kafka的生产者 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 3 ...
- kafka生产者和消费者流程
前言 根据源码分析kafka java客户端的生产者和消费者的流程. 基于zookeeper的旧消费者 kafka消费者从消费数据到关闭经历的流程. 由于3个核心线程 基于zookeeper的连接器监 ...
- JAVA封装消息中间件调用一(kafka生产者篇)
这段时间因为工作关系一直在忙于消息中间件的发开,现在趁着项目收尾阶段分享下对kafka的一些使用心得. kafka的原理我这里就不做介绍了,可参考http://orchome.com/kafka/in ...
随机推荐
- spring 动态bean注册
1. import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory. ...
- 第一部分 JavaScript语言核心(一)
第二章 词法结构 P25 JavaScript是区分大小写的语言:但HTML不区分大小写(XHTML区分大小写,但浏览器具有强大的纠错能力.) 第三章 类型.变量和值 P32 var(variable ...
- JSP标签 <fmt:formatDate>格式化日期
<fmt:formatDate>标签用于使用不同的方式格式化日期. <%@ page language="java" contentType="text ...
- 996.ICU 爆发,互联网从业者难逃“高薪陷阱”
从 3 月 27 日开始,截止本文发稿,GitHub 上面的项目 996.ICU 的 Star 数量已经超过 18 万,这场由程序员发动的轰轰烈烈的公开反对 996 工作制的运动,早已突破互联网圈层而 ...
- C++ createprocess 打开word
#define FileName _TEXT("E:\\DuplicateHandle伪句柄与实句柄的应用.docx") void CMFCApplication1Dlg::OnB ...
- Python学习——enumerate
enumerate(seq, start) seq -- 可遍历的序列 start -- 下标起始位置 seq = [11,22,33,44,55] for i in seq: print( ...
- 十九、CI框架之数据库操作delete用法
一.代码如下: 二.执行f访问 三.查看数据库,已经id=15的数据已经被删掉了 不忘初心,如果您认为这篇文章有价值,认同作者的付出,可以微信二维码打赏任意金额给作者(微信号:382477247)哦, ...
- SpringMVC使用可以访问静态资源,但是导致Controller访问失败
如果在web.xml 拦截配置如下: <!-- Spring MVC servlet --> <servlet> <servlet-name>SpringMVC&l ...
- HZNU-ACM寒假集训Day10小结 单调栈-单调队列
数据结构往往可以在不改变主算法的前提下题高运行效率,具体做法可能千差万别,但思路却是有规律可循 经典问题:滑动窗口 单调队列O(n) POJ 2823 我开始写的: TLE 说明STL的库还是有点慢 ...
- consul配置
参考:https://blog.csdn.net/achenyuan/article/details/80389410 gcp: consul安装目录:/usr/local/bin/consul co ...