- String topic
消息所属的主题。
- Integer partition
消息所在主题的队列数,可以人为指定,如果指定了 key 的话,会使用 key 的 hashCode 与队列总数进行取模来选择分区,如果前面两者都未指定,则会轮询主题下的所有分区。
- Headers headers
该消息的额外属性对,与消息体分开存储.
- K key
消息键,如果指定该值,则会使用该值的 hashcode 与 队列数进行取模来选择分区。
- V value
消息体。
- Long timestamp
消息时间戳,根据 topic 的配置信息 message.timestamp.type 的值来赋予不同的值。
- CreateTime
发送客户端发送消息时的时间戳。
- LogAppendTime
消息在 broker 追加时的时间戳。
其中Headers是一系列的 key-value 键值对。
在了解 ProducerRecord 后我们开始来探讨 Kafka 的消息发送流程。
KafkaProducer 的 send 方法,并不会直接向 broker 发送消息,kafka 将消息发送异步化,即分解成两个步骤,send 方法的职责是将消息追加到内存中(分区的缓存队列中),然后会由专门的 Send 线程异步将缓存中的消息批量发送到 Kafka Broker 中。
消息追加入口为 KafkaProducer#send
public Future send(ProducerRecord<K, V> record, Callback callback) {
// intercept the record, which can be potentially modified; this method does not throw exceptions
ProducerRecord<K, V> interceptedRecord = this.interceptors.onSend(record); // @1
return doSend(interceptedRecord, callback); // @2
}
代码@1:首先执行消息发送拦截器,拦截器通过 interceptor.classes 指定,类型为 List< String >,每一个元素为拦截器的全类路径限定名。
代码@2:执行 doSend 方法,后续我们需要留意一下 Callback 的调用时机。
接下来我们来看 doSend 方法。
2.1 doSend
KafkaProducer#doSend
ClusterAndWaitTime clusterAndWaitTime;
try {
clusterAndWaitTime = waitOnMetadata(record.topic(), record.partition(), maxBlockTimeMs);
} catch (KafkaException e) {
if (metadata.isClosed())
throw new KafkaException(“Producer closed while send in progress”, e);
throw e;
}
long remainingWaitMs = Math.max(0, maxBlockTimeMs - clusterAndWaitTime.waitedOnMetadataMs);
Step1:获取 topic 的分区列表,如果本地没有该topic的分区信息,则需要向远端 broker 获取,该方法会返回拉取元数据所耗费的时间。在消息发送时的最大等待时间时会扣除该部分损耗的时间。
温馨提示