源码分析kafka是如何发送消息的(一)

介绍:

     从这章开始讲kafka生产者。首先讲的是kafka的发送消息流程。kafka发送消息的流程和rocketmq有点类似,无非就是创建网络连接,获取kafka集群broker 的ip port地址,topic 的分区partition信息,选择一个分区partition然后将消息发送到该分区partition对应的broker上。事实上是不是这样的呢?

 一条消息是如何发送到kafka的?

     上面我们简单说了一下kafka发送消息的流程,现在让我们看一下一个发送消息的demo,如下图,创建一个kafkaProducer对象,指定kafka地址,创建一条消息message,指定mesage的topic,调send方法将消息发送出去。

  Properties properties=new Properties();
        properties.put("bootstrap.servers","localhost:9092");//kafkabroker 地址
        properties.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
        properties.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
        properties.put("acks","all");//ISR集合都写入才返回
//        properties.put("enable.idempotence",true);
        properties.put("batch.size","1");//
        KafkaProducer<String,String>  producer=new KafkaProducer(properties);
        ProducerRecord record = new ProducerRecord("topic-test-1", "test-2", "value");//消息
        Future<RecordMetadata> value = producer.send(record);//send 这里异步发送
        RecordMetadata recordMetadata = value.get();//获取结果
        producer.flush();
        System.out.println("ProducerDemo send result ,  topic="+recordMetadata.topic()+" ,partition="
                +recordMetadata.partition()+", offset="+recordMetadata.offset());

      调producer.send方法之后到底发生了什么呢?  client是如何获取topic所有分区信息的呢?topic又会选择topic哪个分区发送消息呢?消息是同步发送到kafka还是异步发送到kafka呢?我们下面进入 send方法看一下:

  @Override
    public Future<RecordMetadata> send(ProducerRecord<K, V> record) {
        return send(record, null);
    }
 
public Future<RecordMetadata> 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 == null ? record : this.interceptors.onSend(record);
        return doSend(interceptedRecord, callback);
    }

   从上面可以看出调用过程,我们发现有个 ProducerInterceptors 拦截器,这个拦截器提供了几个方法,会在消息发送前,和发送后执行,如果我们需要在消息发送前或发送成功后做点事情,可以实现这个拦截器。

 接着往下看,未完待续。。。。

 private Future<RecordMetadata> doSend(ProducerRecord<K, V> record, Callback callback) {
        TopicPartition tp = null;
        try {
            // first make sure the metadata for the topic is available
            long waitedOnMetadataMs = waitOnMetadata(record.topic(), this.maxBlockTimeMs);
            long remainingWaitMs = Math.max(0, this.maxBlockTimeMs - waitedOnMetadataMs);
            byte[] serializedKey;
            try {
                serializedKey = keySerializer.serialize(record.topic(), record.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");
            }
            byte[] serializedValue;
            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");
            }
            int partition = partition(record, serializedKey, serializedValue, metadata.fetch());
            int serializedSize = Records.LOG_OVERHEAD + Record.recordSize(serializedKey, serializedValue);
            ensureValidRecordSize(serializedSize);
            tp = new TopicPartition(record.topic(), partition);
            long timestamp = record.timestamp() == null ? time.milliseconds() : record.timestamp();
            log.trace("Sending record {} with callback {} to topic {} partition {}", record, callback, record.topic(), partition);
            // producer callback will make sure to call both 'callback' and interceptor callback
            Callback interceptCallback = this.interceptors == null ? callback : new InterceptorCallback<>(callback, this.interceptors, tp);
            RecordAccumulator.RecordAppendResult result = accumulator.append(tp, timestamp, serializedKey, serializedValue, interceptCallback, remainingWaitMs);
            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();
            }
            return result.future;
            // handling exceptions and record the errors;
            // for API exceptions return them in the future,
            // for other exceptions throw directly
        } catch (ApiException e) {

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值