Kafka Produer发送消息的基本原理
Kakfa Producer发送消息的流程
Kakfa生产者客户端的整体架构,如下图所示
主线程和Sender线程协调运行。在主线程中由KakfaProducer创建消息,然后经过拦截器、序列化器、分区器的左右之后,缓存消息累加器中。最后通过Sender线程负责将消息发送到Kafka中。
RecordAccumulator缓存消息
RecordAccumulator
主要用来缓存消息,使得Sender线程可以批量发送消息,这样可以减少网络传输资源的消耗,提升性能。RecordAccumulator
缓存的默认大小是32MB,可以通过buffer.memory
进行配置。如果生产者发送消息的速度超过发送到Kakfa的速度,就会导致缓存的空间不足,此时send()
方法被调用的时候就会被阻塞或者抛出异常,这个取决于参数max.block.ms
,默认是60秒。
主线程发送的消息会被添加到RecordAccumulator
的某个双端队列中,队列中的内容就是ProducerBatch
。写入消息时,直接追加到双端队列的尾部。Sender读取消息时,从双端队列头部开始读取。在ProducerBatch
中包含多个ProducerRecord
,消息批量发送,可以减少网络请求的次数以提高系统整体的吞吐量。消费者客户端如果需要向很多分区发送消息,可以调整buffer.memory
参数调整系统的吞吐量。
ProducerBatch
的大小可以通过batch.size
参数来配置,当一条消息写入RecordAccumulator
时,会首先寻找消息分区对应的双端队列Deque
,如果没有就会创建一个新的队列,然后在从队列尾部获取一个ProducerBatch
,判断是否还有空间写入这个ProducerRecord
,如果可以写入就直接写入到batch中即可,没有就会创建一个新的batch,并且在创建的时候,会判断这个消息的大小是否超过batch.size
,如果超过就会创建对应大小的Batch,并且对应的区域不会复用,所以如果在线上系统发现每次创建的消息都比batch.size
大,就需要适当调高这个参数的值,使得可以复用内存区域。
Sender线程发送消息
Sender线程从缓冲区中获取到缓存的消息之后,会转化为<分区,Deque<ProducerBatch>>
,然后进一步转化为<Node,List<ProducerBatch>>
的形式,Node表示Kakfa集群中的一个节点。因为网络连接是对应于某个机器而言的,也就是只关心向具体的broker发送消息,并不关心是那个分区。
然后会进一步把请求封装成<Node,Request>
,然后就将每个请求发送到各个Broker上。在发送之前还会将请求放入到InFlightRequests
中,InFlightRequests
对象具体保存的形式是Map<NodeId,Deque<Request>>
。InFlightRequests
的作用是用来缓存发送出去但还没有收到响应的请求。可以通过这个参数,来判断当前的网络请求。max.in.flight.requests.per.connection
默认值是5,最多只能缓存5个未响应的请求,超过该参数之后就不能再发送更多的请求,除非有缓存的请求收到了响应。如果超过就说明对应的Broker负载较大或者网络连接有问题。