Kafka2.7 Producer API

349 阅读4分钟

记录Java使用Kafka Producer API的一些实现细节,主要是一些生产者参数的设置和要求等,以及相关API方法的使用。

Producer API使用方法

引入依赖

<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-clients</artifactId>
    <version>2.7.0</version>
</dependency>

发布-订阅模式

kafka消费以消费者组为主题,要实现发布-订阅模式,只需要设置不同的消费者组名称,则所有订阅指定topic的消费者组均能消费此topic的消息。

点对点模式

点对点模式适用于单个消费者,且常常伴有保持消息推送顺序的需求。生产者通过设置Key可以保证相同Key值的消息被发送到相同的分区中,而每个分区均只能被消费者组中的一个消费者消费,从而实现点对点模式。需要额外注意的是,生产者的重试机制可能会打乱消息提交的顺序,如果对消息提交顺序有严格要求的话,需要额外设置生产者的配置参数max.in.flight.requests.per.connection=1

简单范例

Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.1.108:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");

try (Producer<String, String> producer = new KafkaProducer<>(props)) {
    ProducerRecord<String, String> record = new ProducerRecord<>("mytopic""我是一条渺小的消息");
    producer.send(record);
}

回调函数的使用

Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.1.110:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");

try (Producer<String, String> producer = new KafkaProducer<>(props)) {
    ProducerRecord<String, String> record = new ProducerRecord<>("mytopic""我是一条渺小的消息");
    producer.send(record, (RecordMetadata metadata, Exception e) -> {
        if (e != null) {
            System.out.println("系统错误: " + e.getMessage());
        } else {
            System.out.println(String.format("消息发送成功, 消息位移为[%d], 分区为[%d]", metadata.offset(), metadata.partition()));
        }
    });
}

开启压缩

生产者使用压缩只需要添加下面配置即可

props.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, "lz4");

需要留意的是,在使用压缩算法前需要留意broker是否有启用压缩,如果有应尽量保持使用的算法一致,避免额外的解压缩。broker端的压缩配置名称与producer一致,均为compression.type

事务型生产者

Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.1.108:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true);
props.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, "TestTranId");

try (Producer<String, String> producer = new KafkaProducer<>(props)) {
    try {
        producer.initTransactions();
        producer.beginTransaction();
        for (int i = 0; i < 3; i++) {
            ProducerRecord<String, String> record = new ProducerRecord<>("mytopic""key-" + i, "value-" + i);
            producer.send(record, ((metadata, e) -> {
                if (e != null) {
                    System.out.println("发送失败:" + e.getMessage());
                } else {
                    System.out.println("发送分区为:" + metadata.partition());
                }
            }));
        }
        producer.commitTransaction();
    } catch (KafkaException e) {
        producer.abortTransaction();
    }
}

关于kafka producer你应该知道的事情

  • 生产者是线程安全的,跨线程共享一个生产者实例通常比拥有多个实例要快。

The producer is thread safe and sharing a single producer instance across threads will generally be faster than having multiple instances.

  • Kafka消息交付可靠性保障

    • 至少一次 kafka默认提供至少一次的可靠性保障,只需要设置生产者参数 acks=all即可实现。当提交消息写入broker但broker在返回success时故障导致生产者重试,则可能会产生重复数据。
    • 最多一次 禁用重试机制,即可实现最多一次的可靠性保障。
    • 精确一次 要实现精确一次的最可靠的方式是使用事务型生产者,其次是使用幂等性生产者。相对于事务型生产者,幂等性生产者具有这些缺点:幂等性生产者只能保证单分区上的幂等性,即一个幂等性生产者能够保证某个主题的一个分区上不出现重复消息,它无法实现多个分区的幂等性。其次,它只能实现单会话上的幂等性,不能实现跨会话的幂等性(单会话意味着生产者线程不能重启,否则幂等性保证失效)。

kafka producer常用配置详解

bootstrap.servers

用于与kafka集群建立连接的主机:端口列表(CSV格式),建议在此列表中填入多台broker地址,防止服务器宕机带来不好的影响。

key.serializer & value.serializer

kafka允许用户自定义消息传输的格式,key.serializervalue.serializer正是用来将键值消息转换成统一格式传输的序列化工具,与此对应的反序列化属性为 key.deserializer & value.deserializer 。

acks

默认值:1
kafka采用副本机制来实现消息的冗余,以此避免消息的丢失。当producer向broker发送消息时,broker需要将消息记录到ISR中的所有副本中,并告诉producer是否已经记录成功。acks可能为3个值(String):

  • acks=0:producer视为发送成功,不等待结果返回。且重试(retries)机制会失效,因为producer往往不知道故障是否发生。
  • acks=1:当leader副本成功记录下消息,producer视为发送成功。若在leader记录成功但未同步到follower中时,leader故障,则消息将会丢失。
  • acks=all: 只有ISR中的所有副本都成功过记录下消息时,producer才认为发送成功。因此只要有一个副本存活,消息就不会丢失。

compression.type

默认值:none
压缩算法,可选的有:nonegzipsnappylz4zstd。算法的选择可根据下面的关系进行设置:

吞吐量方面:lz4 > snappy > zstd 和 gzip  
压缩比方面,zstd > lz4 > gzip > snappy  

retries

默认值:Integer.MAX_VALUE(2147483647)
发送的重试次数,通常情况下不需要修改,但需要知道当max.in.flight.requests.per.connection不为1时,重试机制将可能导致消息顺序改变。

transactional.id

用于事务型生产者开启事务,若设置了该属性,则enable.idempotence也应该设置为true。另外开发环境下kafka集群大小小于3时,需要调整transaction.state.log.replication.factor的值,否则内部主题将会创建失败。