Kafka 之 Kafka从源码角度解读Kafka send

​这一期我们来到了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的流程:

在这里插入图片描述
最后感觉大家关注我的微信公众号:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值