基本知识
- 我们所说的消息本质是ProducerRecord中的一个value属性。除此之外,还应当包括主题,分区号,消息头部,键,消息的时间戳。
- 建可以用来计算分区号,从而发送特定的区
- Producer本质是线程安全的,多线程可共享实例
发送消息的三种形式
- 发送即忘
- 同步
- 异步
分区器
- 如果没有指定partition,则要通过key计算,发往哪个分区
- 默认的分区器会对key进行哈希计算来判断发往哪个分区(所有分区)
- 如果key为null,则以轮询的方式发往可用分区
- 如果主题新增分区,映射关系可能发生改变
拦截器
- 规则过滤消息
- 统计消息数量
- 定制化需求
原理分析
整体流程
- 客户端由俩个线程协调运行
- 主线程:由Producer创建消息,然后通过拦截器,序列化器,分区器,之后缓存到消息累加器(RecordAccumulator)。
- Sender线程:从RecordAccumulator中获取消息并发送到Kafka
- RecordAccumulator主要来缓存消息以便Sender线程批量发送消息,进而减少网络传输的资源消耗。
- 默认空间大小为32MB,如果满了之后,要不阻塞,要不抛异常。具体看配置,默认阻塞60s。
- 主线程的消息会被追加到RecordAccumulator的某个双端队列。内部为每个分区都维护了一个双端队列。内容是ProducerBatch。
- 生产的消息会被追加到ProducherBatch的尾部,而消费者从头部读取。
- ProducerBacth可以包含多个ProducerRecord,可以理解为一个消息批次。作用就是消息更加紧凑,减少传输。
- BufferPool主要用来开辟一块内存用来保存对应的消息,可以复用。不过只针对设定大小来进行复用。
- 一条消息在进入RecordAccmulator会先查看是否分区所对应的双端队列,没有则新建。从队列的尾部获取ProducerBacth,没有则新建。是否可以写入,不可以写入,依靠默认参数大小创建新的ProducerBacth。使用之后可以通过BufferPool来进行复用,如果超过大小则不复用。
- Sender从RecordAccumulator中获取缓存的消息之后,会进一步将原本**<分区,Deque>的保存形式转变成<Node,List>**的形式。
- Node表示Kafka集群的Broker节点。这里做一个应用逻辑层到网络I/O层面的转换。
- 转换之后,进一步封装成**<Node,Request>的形式,这样就可以将Request请求发往到各个Node**。
- Sender发往Kakfa之前还会保存到InFlightRequests。
- 保存对象的具体形式是Map<NodeId,Deque>,他的主要作用是缓存已经发出去但还没有收到响应的请求。NodeId是节点的编号。
元数据
元数据是指Kafka集群中的元数据,这些数据记录了集群中有哪些主题,主题有哪些分区,每个分区的节点分布及节点信息