以下为个人总结,以官网为准
如有错误,欢迎指出
-
Kafka生产者有两个线程,一个为主线程,一个为Sender线程(它是一个守护线程)
-
消息发送后首先经历拦截器,在拦截器中可以对消息做一些统一的操作,比如:加上统一的标识
-
然后会被序列化为字节流
-
到达分区器,分区器主要是对消息去哪个分区做规划
-
做好分区规划后,消息到达了消息累加器,累加器是一个双端队列(每一个队列对应一个分区),然后将消息封装到ProducerBatch,ProducerBatch也是一个多消息的容器,它是可以不断想里面加入消息的。让消息变成一批次,一批次的。
-
Sender线程主动从消息累加器中取消息,然后将上一步Deque<partitionId,ProducerBatch>转换为<Node,List>的形式,然后再将其转换为<Node,Request>,这一步是将Kafka消息从逻辑上转换为物理上的主要转折点。从这步开始,就不会关心分区这个逻辑概念,只会注意要发向哪一台机器了
-
在发送之前,还会将消息转换为Map<Node,Dequen【Requst】>
保存在InFlightRequst中,表示消息发送等待回应
-
消息发最终由Selector发送
一些细节
- 消息累加器的双端队列让写入和读取效率互不影响,从头写入,sender线程从末尾读取
- 消息累加器的ProducerBatch实际是一个缓存,由BufferPool管理。统一管理制定的切片缓存,使用完后还可以复用。缓存统一切成一样大小的小片的优势为减少了不同大小缓存的申请销毁的开销。这是一种减少开销的缓存使用方案,Hbase写入数据时同样也用到一个叫ChunkPool的管理器。在实际的应用中,如果有一条消息写入消息累加器后,首先找到对应的分区队列,然后评估自己的大小,然后获取ProducerBatch。如果放的下(ProducerBatch对应前面所说的缓存切片),就向ProducerBatch中加入,如果大于了默认的切片大小(重新申请一个也无法满足的情况),那就单独申请内存。但是这种单独申请的内存是不能被回收复用的。
- LeastLoadNode ,这是一个在InFlightRequest中的概念,它表示压力最小的那个节点。通过在InflightRequest中等待的每个节点消息数的统计来确定向那个压力最小的节点发送消息,并且这个节点也是生产者发送元数据请求的节点。
消息分区机制
-
如果手动指定分区号,发送到指定的分区
-
如果没有指定分区号,指定了key值,key的hashcode%分区数
-
如果以上都没有,那么轮询机制
[外链图片转存中…(img-1bIXz38U-1614870458807)]