这一期我们来到了Kafka我们都知道:Kafka是一个分布式消息队列。具有高性能、持久化、多副本备份、横向扩展能力。生产者往队列里写消息,消费者从队列里取消息进行业务逻辑。一般在架构设计中起到解耦、削峰、异步处理的作用。
我们今天从Kafka的官方Demo来探一探Kafka的究竟。
我们先在本地下载和编译好kafka的源码,然后他的目录层级大概是这样:
OK,我们进入到官方给的examples目录里面:
然后我们点开Producer类,前面是初始化一些配置信息,后面是一个线程的run方法,在run方法内部在一直发送信息。
然后我们点进去send方法看看内部的实现:然后是到了KafkaProducer内部的send方法,但是在send方法里实际上是执行了doSend方法.
ok,我们在点进去doSend方法:
进来了doSend的方法里,第一件事是( first make sure the metadata for the topic is available)即拉取元数据。
ClusterAndWaitTime clusterAndWaitTime = waitOnMetadata(record.topic(), record.partition(), maxBlockTimeMs);
然后对发送的消息的key和value进行序列化。
try {
serializedKey = keySerializer.serialize(record.topic(), record.key());//对key进行序列化
} catch (ClassCastException cce) {
throw new SerializationException("Can't convert key of class " + record.key().getClass().getName() +
" to class " + producerConfig.getClass(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG).getName() +
" specified in key.serializer");
try {
serializedValue = valueSerializer.serialize(record.topic(), record.value());
} catch (ClassCastException cce) {
throw new SerializationException("Can't convert value of class " + record.value().getClass().getName() +
" to class " + producerConfig.getClass(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG).getName() +
" specified in value.serializer");
}
然后我们回到刚刚的那个Kafka的Demo,看看我们配置了什么序列化器。
这里可以看出来是指定了key为IntegerSerializer,value为StringSerializer.
然后来到这里:根据消息来计算分区,然后计算消息是否超过了定义的消息最大值
int partition = partition(record, serializedKey, serializedValue, cluster);
int serializedSize = Records.LOG_OVERHEAD + Record.recordSize(serializedKey, serializedValue);
ensureValidRecordSize(serializedSize);
到这里我们去确认一下默认的消息最大值:1M
.define(MAX_REQUEST_SIZE_CONFIG,
Type.INT,
1 * 1024 * 1024,
atLeast(0),
Importance.MEDIUM,
MAX_REQUEST_SIZE_DOC)
然后继续往下走:
这里是将刚刚算好的分区和主题来封装TopicPartition对象。
然后绑定回调函数:
Callback interceptCallback = this.interceptors == null ? callback : new InterceptorCallback<>(callback, this.interceptors, tp);
然后将我们的TopicPartition对象append到RecordAccumulator(累加器,这里提前剧透一下,这个RecordAccumulator是放在内存的,默认大小32M,其目的是封装batch)里面,将一批数据封装成一个batch。
我们再进去RecordAccumulator这个对象,查看他的一部分属性。
this.accumulator = new RecordAccumulator(config.getInt(ProducerConfig.BATCH_SIZE_CONFIG),
this.totalMemorySize,
this.compressionType,
config.getLong(ProducerConfig.LINGER_MS_CONFIG),
retryBackoffMs,
metrics,
time);
在这里初始化了一个RecordAccumulator对象,totalMemorySize为32M:
define(BUFFER_MEMORY_CONFIG, Type.LONG, 32 * 1024 * 1024L, atLeast(0L), Importance.HIGH, BUFFER_MEMORY_DOC)
然后会判断这个batch是否数据填充满了,如果填充满了就唤醒线程发送数据.
if (result.batchIsFull || result.newBatchCreated) {
log.trace("Waking up the sender since topic {} partition {} is either full or getting a new batch", record.topic(), partition);
this.sender.wakeup();
}
最后总结一下整个send的流程:
最后感觉大家关注我的微信公众号: