1、Producer代码实现 

ps:不建议使用自定义序列化和反序列化,他们会把生产者和消费者耦合在一起,且容易出错

// 同步发送消息、// 异步发送消息
public class KafkaProducerDemo {
    private static Properties prop;
    private static KafkaProducer<String, String> producer;

    private static final String TOPIC_NAME = "topic-07";

    static {
        // 具体的参数名可以从CommonClientConfigs、ProducerConfig获取
        prop = new Properties();
        prop.put("bootstrap.servers", "192.168.111.129:9092,192.168.111.129:9093,192.168.111.129:9094");
        prop.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        prop.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        prop.put("acks", "-1");// acks=0 表示不关系是否发送成功;acks=1表示leader写入成功;acks=-1或acks=all表示所有副本写入成功
        prop.put("retries", 0);
        prop.put("batch.size", 16384);// 消息发送批次大小,16KB,消息大小超过这个大小了,就发送
        prop.put("linger.ms", 1);// 消息发送间隔时间,1ms,等待时间到了,不管消息大小是否满足,就发送
        prop.put("buffer.memory", 33554432);
        producer = new KafkaProducer<>(prop);
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            syncSendMessage(i+"");
        }
        //asyncSendMessage();
    }
    /**
     * 异步发送消息
     **/
    private static void asyncSendMessage() {
        ProducerRecord<String, String> record = new roducerRecord<>(TOPIC_NAME, "kafka third message");
        try {
            Future<RecordMetadata> result = producer.send(record, new Callback() {
                @Override
                public void onCompletion(RecordMetadata metadata, exception exception) {
                    if (exception != null) {
                        exception.printStackTrace();
                    }
                }
            });
            System.out.println("分区:" + result.get().partition() + ", offset:" + result.get().offset());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 同步发送消息
     **/
    private static void syncSendMessage(String content) {
        ProducerRecord<String, String> record = new ProducerRecord<>(TOPIC_NAME, content, "kafka second message");
        try {
            RecordMetadata result = producer.send(record).get();
            System.out.println("分区:" + result.partition() + ", offset:" + result.offset());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2、分区器

  • 客户端可以控制将消息发送到哪个分区
  • Kafka默认分区机制(org.apache.kafka.clients.producer.internals.DefaultPartitioner):
  • 如果记录中指定了分区,则直接使用
  • 如果未指定分区,但指定了key值,则根据key的hash值选择一个分区(相同的key所发送到的Partition是同一个,可用来保证消息的局部有序性)
  • 如果未指定分区,也未指定key值,则以 '黏性分区' 策略(2.4版本以前使用轮询策略)选择一个分区
  • 分区策略
  • 轮询策略(org.apache.kafka.clients.producer.RoundRobinPartitioner)
  • 如果key值为null,并且使用了默认的分区器,Kafka会根据轮训(Random Robin)策略将消息均匀地分布到各个分区上。
  • 散列策略(Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions)
  • 如果键值不为null,并且使用了默认的分区器,Kafka会对键进行散列,然后根据散列值把消息映射到对应的分区上
  • 黏性分区策略(org.apache.kafka.clients.producer.UniformStickyPartitioner)
  • 很多时候消息是没有指定Key的。而Kafka 2.4之前的策略是轮询策略,这种策略在使用中性能比较低。所以2.4中版本加入了黏性分区策略(Sticky Partitioning Strategy)。
  • 黏性分区器(Sticky Partitioner)主要思路是选择单个分区发送所有无Key的消息。一旦这个分区的batch已满或处于“已完成”状态,黏性分区器会随机地选择另一个分区并会尽可能地坚持使用该分区——象黏住这个分区一样
  • 自定义策略
  • 默认分区器是使用次数最多的分区器。除了散列分区之外,用户可以根据需要对数据使用不一样的分区策略
  • 实现org.apache.kafka.clients.producer.Partitioner接口,在配置中设置实现的类prop.put("partitioner.class", 实现类);