一般发送数据到Kafka,就是new一个producer,然后简单到send就好了,就可以在kafka里面看到数据了,可以在Linux的命令行模式下,使用命令行的消费命令消费到:
sh /xx/xx/kafka/bin/kafka-console-consumer.sh --bootstrap-server 1.1.1.1:92 --topic topic_name 
但是你在写测试代码的时候,想看看数据是否真的写到kafka服务器了的时候,就发现,额,好像数据没发送到kafka服务器上似的,代码也看似没问题哦。Java代码如下:

private void produceOnce(String toTopic) {
        Properties conf = new Properties();
        conf.setProperty(BOOTSTRAP_SERVERS_CONFIG, "1.1.1.1:2");
        conf.put(ACKS_CONFIG, "all");
        conf.put(KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
        conf.put(VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
        KafkaProducer<String, String> producer = new KafkaProducer<>(conf);
        List<String> list = Lists.newArrayList(
                "aaaa","bbb"
        );
        for (String v : list) {
            ProducerRecord<String, String> record = new ProducerRecord<>(toTopic, "k", v);
            try {
                //producer.send(record);//异步发送
                producer.send(record).get();//等待每次的发送结果,同步的了。
            } catch (Exception e) {
                System.out.println(e.toString());
            }
        }
    }

注意,在使用producer发送数据的时候,有2行,一个是send(),一个send().get()。我把send给注释了,留下了带get的方法,在junit的测试方法中,使用简单的直接send,数据可能就发不到kafka去。带上get之后,就能发到kafka去。具体原因就跟这个kafka的producer的原理有关系了。

kafka send异步阻塞 kafka producer 异步_kafka send异步阻塞

调用send方法,并不是说这个消息就会被发送到kafka服务器,他为了效率他只是把你这个消息 record暂时给发到一个队列去,上图的accumulator单词意思:n. 蓄电池;[计] 累加器;积聚者。。等待kafka的线程来发送。可能这个线程还没好呢,junit方法的test方法已经走完了,jvm已经结束了,那个发送数据的kafka的线程也就gg了,但是get之后,他是需要等待发送的返回结果的,所以,在测试的时候,这个方法就能保证数据发到kafak服务器去了,而不是缓存在本地的队列中。

kafka send异步阻塞 kafka producer 异步_kafka send异步阻塞_02

异步发送

Kafka 自从 0.8.2 版本就引入了新版本 Producer API,新版 Producer 完全是采用异步方式发送消息。生产端构建的 ProducerRecord 先是经过 keySerializer、valueSerializer 序列化后,再是经过 Partition 分区器处理,决定消息落到 topic 具体某个分区中,最后把消息发送到客户端的消息缓冲池 accumulator 中,交由一个叫作 Sender 的线程发送到 broker 端。

缓冲池最大大小由参数buffer.memory控制,默认是32M,当生产消息的速度过快导致buffer满了的时候,将阻塞max.block.ms时间,超时抛异常,所以buffer的大小可以根据实际的业务情况进行适当调整。

批量发送

发送到缓冲池中消息将会被分为一个一个的batch,分批次的发送到broker 端,批次大小由参数batch.size控制,默认16KB。这就意味着正常情况下消息会攒够16KB时才会批量发送到broker端,所以一般减小batch大小有利于降低消息延时,增加batch大小有利于提升吞吐量。

但是消息并不是必须要达到一个batch尺寸才会批量发送到服务端呢,Producer端提供了另一个重要参数linger.ms,用来控制batch最大的空闲时间,超过该时间的batch也会被发送到broker端。

整个流程大概分为如下几步

      1、构建一个KafkaProducer对象,初始化一些用到的组件,比如缓存区,Sender线程等

      2、如果配置了拦截器,可用对发送的消息进行可定制化的拦截或更改

      3、对Key,value进行序列化

      4、根据传入的参数,为消息选择合适的分区,具体怎么选,后面分析

      5、将消息按照分区发送到RecordAccmulator暂存,消息按照每个分区进行汇总

      6、后台Sender线程被触发后从RecordAccmulator里面获取消息然后构建成ClientRequest,怎么构建后面分析

      7、将ClientRequest封装成NetWorkClient准备发送

      8、NetWorkClient将请求放入KafkaChannel准备发送,然后执行网络IO,最后发送到kafka server

kafka send异步阻塞 kafka producer 异步_java_03

补充:

由上面的理论,可以看到数据是先去一个本地缓存的地方,等着被kafka自己的线程去实际发送数据到kafka,我们的简单的send,只是把数据发到本地缓存了,解决这个问题的方法,除了在send之后再get一下之外,kafka还提供了close方法。这个close方法就是会堵塞,直到实际发送完成,才真正退出。下面是某源码描述文字。

kafka send异步阻塞 kafka producer 异步_kafka send异步阻塞_04

要是在上面代码,最后来个close方法,那么,在使用send方法的时候,我这个测试数据就会最终入到kafka了,就不会看到这个奇怪的现象了。

不过,在实际使用kafka producer的时候,也不会闲的没事儿给他close了,也不会调用那个send().get(),还是用简单的send()。毕竟这个效率高。