// 生产者会从给定的 broker 里查找到其它 broker 的信息。——建议至少提供两个 broker 的信息,因为一旦其中一个宕机,生产者仍然能够连接到集群上。
 props.put(“bootstrap.servers”, brokerList);
 // 将 key 转换为字节数组的配置,必须设定为一个实现了 org.apache.kafka.common.serialization.Serializer 接口的类,
 // 生产者会用这个类把键对象序列化为字节数组。
 // ——kafka 默认提供了 StringSerializer和 IntegerSerializer、 ByteArraySerializer。当然也可以自定义序列化器。
 props.put(“key.serializer”, “org.apache.kafka.common.serialization.StringSerializer”);
 // 和 key.serializer 一样,用于 value 的序列化
 props.put(“value.serializer”, “org.apache.kafka.common.serialization.StringSerializer”);
 // 用来设定KafkaProducer对应的客户端ID,默认为空,如果不设置KafkaProducer会自动 生成一个非空字符串。
 // 内容形式如:“producer-1”
 props.put(“client.id”, “producer.client.id.demo”);
 return props;
 }Properties props = initConfig();
 KafkaProducer<String, String> producer = new KafkaProducer<>(props);
 // KafkaProducer<String, String> producer = new KafkaProducer<>(props,
 // new StringSerializer(), new StringSerializer());
 //生成 ProducerRecord 对象,并制定 Topic,key 以及 value
 ProducerRecord<String, String> record = new ProducerRecord<>(topic, “hello, Kafka!”);
 try {
 // 发送消息
 producer.send(record);
 }

3.发送类型

发送即忘记

producer.send(record)

同步发送

//通过send()发送完消息后返回一个Future对象,然后调用Future对象的get()方法等待kafka响应
//如果kafka正常响应,返回一个RecordMetadata对象,该对象存储消息的偏移量
// 如果kafka发生错误,无法正常响应,就会抛出异常,我们便可以进行异常处理
producer.send(record).get();

异步发送

producer.send(record, new Callback() {
 public void onCompletion(RecordMetadata metadata, Exception exception) {
 if (exception == null) {
 System.out.println(metadata.partition() + “:” + metadata.offset());
 }
 }
 });

4.序列化器

消息要到网络上进行传输,必须进行序列化,而序列化器的作用就是如此。

Kafka 提供了默认的字符串序列化器(org.apache.kafka.common.serialization.StringSerializer),还有整型(IntegerSerializer)和字节数组(BytesSerializer)序列化器,这些序列化器都实现了接口(org.apache.kafka.common.serialization.Serializer)基本上能够满足大部分场景的需求。

5.自定义序列化器

见代码库:com.heima.kafka.chapter2.CompanySerializer

/**
• 自定义序列化器
 */
 public class CompanySerializer implements Serializer {
 @Override
 public void configure(Map configs, boolean isKey) {
 }@Override
 public byte[] serialize(String topic, Company data) {
 if (data == null) {
 return null;
 }
 byte[] name, address;
 try {
 if (data.getName() != null) {
 name = data.getName().getBytes(“UTF-8”);
 } else {
 name = new byte[0];
 }
 if (data.getAddress() != null) {
 address = data.getAddress().getBytes(“UTF-8”);
 } else {
 address = new byte[0];
 }
 ByteBuffer buffer = ByteBuffer. allocate(4 + 4 + name.length + address.length);
 buffer.putInt(name.length);
 buffer.put(name);
 buffer.putInt(address.length);
 buffer.put(address);
 return buffer.array();
 } catch (UnsupportedEncodingException e) {
 e.printStackTrace();
 }
 return new byte[0];
 }@Override
 public void close() {
 }
 }
  • 使用自定义的序列化器

见代码库:com.heima.kafka.chapter2.ProducerDefineSerializer

public class ProducerDefineSerializer {
 public static final String brokerList = “localhost:9092”;
 public static final String topic = “heima”;public static void main(String[] args) throws ExecutionException, InterruptedException {
 Properties properties = new Properties();
 properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
 properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, CompanySerializer.class.getName());
 // properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
 // ProtostuffSerializer.class.getName());
 properties.put(“bootstrap.servers”, brokerList);KafkaProducer<String, Company> producer = new KafkaProducer<>(properties);
 Company company = Company.builder().name(“kafka”) .address(“北京”).build();
 // Company company = Company.builder().name(“hiddenkafka”)
 // .address(“China”).telphone(“13000000000”).build();
 ProducerRecord<String, Company> record = new ProducerRecord<>(topic, company);
 producer.send(record).get();
 }
 }

6.分区器

本身kafka有自己的分区策略的,如果未指定,就会使用默认的分区策略:

Kafka根据传递消息的key来进行分区的分配,即hash(key) % numPartitions。如果Key相同的话,那么就会分配到统一分区。

源代码org.apache.kafka.clients.producer.internals.DefaultPartitioner分析

public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
 List partitions = cluster.partitionsForTopic(topic);
 int numPartitions = partitions.size();
 if (keyBytes == null) {
 int nextValue = this.nextValue(topic);
 List availablePartitions = cluster.availablePartitionsForTopic(topic);
 if (availablePartitions.size() > 0) {
 int part = Utils.toPositive(nextValue) % availablePartitions.size();
 return ((PartitionInfo)availablePartitions.get(part)).partition();
 } else {
 return Utils.toPositive(nextValue) % numPartitions;
 }
 } else {
 return Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions;
 }
 }
  • 自定义分区器见代码库 com.heima.kafka.chapter2.DefinePartitioner
/**
• 自定义分区器
 */
 public class DefinePartitioner implements Partitioner {
 private final AtomicInteger counter = new AtomicInteger(0);@Override
 public int partition(String topic, Object key, byte[] keyBytes,Object value, byte[] valueBytes, Cluster cluster) {
 List partitions = cluster.partitionsForTopic(topic);
 int numPartitions = partitions.size();
 if (null == keyBytes) {
 return counter.getAndIncrement() % numPartitions;
 } else return Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions;
 }
 @Override
 public void close() {
 }
 @Override
 public void configure(Map<String, ?> configs) {
 }
 }
  • 实现自定义分区器需要通过配置参数ProducerConfig.PARTITIONER_CLASS_CONFIG来实现

// 自定义分区器的使用
props.put(ProducerConfig.PARTITIONER_CLASS_CONFIG,DefinePartitioner.class.getNam e());

7.拦截器

Producer拦截器(interceptor)是个相当新的功能,它和consumer端interceptor是在Kafka 0.10版本被引入的,主要用于实现clients端的定制化控制逻辑

生产者拦截器可以用在消息发送前做一些准备工作。

使用场景

  1. 按照某个规则过滤掉不符合要求的消息
  2. 修改消息的内容
  3. 统计类需求

见代码库:自定义拦截器com.heima.kafka.chapter2.ProducerInterceptorPrefix

/**

  • 自定义拦截器
    */
    public class ProducerInterceptorPrefix implements ProducerInterceptor<String, String> {
    private volatile long sendSuccess = 0;
    private volatile long sendFailure = 0;