【Kafka】Kafka的API—Producer API、Consumer API和自定义 Interceptor

本文详细介绍了Kafka的Producer API,包括异步和同步发送消息的方式,并给出了示例代码。同时,讲解了Consumer API,讨论了自动提交和手动提交offset的实现。此外,还展示了如何实现自定义Interceptor,通过时间戳和计数拦截器的案例,说明了Interceptor在消息发送前后的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、Producer API

1、异步发送 API

2、同步发送 API

 二、Consumer API

1、自动提交 offset 

2、手动提交 offset

1)同步提交 offset

 三、自定义 Interceptor

1、案例

(1)增加时间戳拦截器 

(2)增加次数拦截器 

 (3)producer 主程序


一、Producer API

Kafka Producer 发送消息采用的是 异步发送 的方式。在消息发送的过程中,涉及到了
两个线程—— main 线程和 Sender 线程 ,以及 一个线程共享变量— RecordAccumulator
main 线程将消息发送给 RecordAccumulator Sender 线程不断从 RecordAccumulator 中拉取消息发送到 Kafka broker

1、异步发送 API

1 )导入依赖
<dependency>
        <groupId>org.apache.kafka</groupId>
        <artifactId>kafka-clients</artifactId>
        <version>0.11.0.0</version>
</dependency>
public static void main(String[] args) throws Exception{
     Properties props = new Properties();
     //kafka 集群,broker-list
     props.put("bootstrap.servers", "hadoop102:9092");
     props.put("acks", "all");
     //重试次数
     props.put("retries", 1);
     //批次大小
     props.put("batch.size", 16384);
     //等待时间
     props.put("linger.ms", 1);
     //RecordAccumulator 缓冲区大小
     props.put("buffer.memory", 33554432);
     props.put("key.serializer", 
    "org.apache.kafka.common.serialization.StringSerializer");
     props.put("value.serializer", 
    "org.apache.kafka.common.serialization.StringSerializer");
     Producer<String, String> producer = new KafkaProducer<>(props);
     // 不带回调函数
     for (int i = 0; i < 100; i++) {
         producer.send(new ProducerRecord<String, String>("first", 
         Integer.toString(i), Integer.toString(i)));
     }

     // 带回调函数
     for (int i = 0; i < 100; i++) {
         producer.send(new ProducerRecord<String, String>("first", 
            Integer.toString(i), Integer.toString(i)), new Callback() {
             //回调函数,该方法会在 Producer 收到 ack 时调用,为异步调用
             @Override
             public void onCompletion(RecordMetadata metadata, Exception exception) {
                 if (exception == null) {
                     System.out.println("success->" + metadata.offset());
                 } else {
                     exception.printStackTrace();
                 }
             }
         });
     }

     producer.close();
}

2、同步发送 API

 同步发送的意思就是,一条消息发送之后,会阻塞当前线程,直至返回 ack实际应用很少,可保证消息队列的有序性。

public static void main(String[] args) throws Exception{
     Properties props = new Properties();
     props.put("bootstrap.servers", "hadoop102:9092");//kafka 集群,broker-list
     props.put("acks", "all");
     props.put("retries", 1);//重试次数
     props.put("batch.size", 16384);//批次大小
     props.put("linger.ms", 1);//等待时间
     props.put("buffer.memory", 33554432);//RecordAccumulator 缓冲区大小
     props.put("key.serializer", 
    "org.apache.kafka.common.serialization.StringSerializer");
     props.put("value.serializer", 
    "org.apache.kafka.common.serialization.StringSerializer");
     Producer<String, String> producer = new KafkaProducer<>(props);
     for (int i = 0; i < 100; i++) {
         producer.send(new ProducerRecord<String, String>("first", 
         Integer.toString(i), Integer.toString(i))).get();
     }
     producer.close();
}

 二、Consumer API

        Consumer 消费数据时的可靠性是很容易保证的,因为数据在 Kafka 中是持久化的,故不用担心数据丢失问题。由于 consumer 在消费过程中可能会出现断电宕机等故障, consumer 恢复后,需要从故障前的位置的继续消费,所以 consumer 需要实时记录自己消费到了哪个 offset ,以便故障恢复后继续消费。 所以 offset 的维护是 Consumer 消费数据是必须考虑的问题。
       

1、自动提交 offset 

        为了使我们能够专注于自己的业务逻辑,Kafka 提供了自动提交 offset 的功能。
自动提交 offset 的相关参数:
        enable.auto.commit 是否开启自动提交 offset 功能
        auto.commit.interval.ms 自动提交 offset 的时间间隔
public static void main(String[] args) {
     Properties props = new Properties();
     props.put("bootstrap.servers", "hadoop102:9092");
     // 消费者组
     props.put("group.id", "test");
     // 开启自动提交
     props.put("enable.auto.commit", "true");
     props.put("auto.commit.interval.ms", "1000");
     props.put("key.deserializer", 
    "org.apache.kafka.common.serialization.StringDeserializer");
     props.put("value.deserializer", 
    "org.apache.kafka.common.serialization.StringDeserializer");
     KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
     consumer.subscribe(Arrays.asList("first"));
     while (true) {
         ConsumerRecords<String, String> records = consumer.poll(100);
         for (ConsumerRecord<String, String> record : records){
             System.out.printf("offset = %d, key = %s, value 
            = %s%n", record.offset(), record.key(), record.value());
     }
}

2、手动提交 offset

        虽然自动提交 offset 十分简介便利,但由于其是基于时间提交的,开发人员难以把握
offset 提交的时机。因此 Kafka 还提供了手动提交 offset API
        手动提交 offset 的方法有两种:分别是 commitSync (同步提交) commitAsync (异步提交) 。两者的相同点是,都会将 本次 poll 的一批数据最高的偏移量提交 ;不同点是,
commitSync 阻塞当前线程,一直到提交成功,并且会自动失败重试(由不可控因素导致,
也会出现提交失败);而 commitAsync 则没有失败重试机制,故有可能提交失败。

1)同步提交 offset

public static void main(String[] args) {
     Properties props = new Properties();
     //Kafka 集群
     props.put("bootstrap.servers", "hadoop102:9092");
     //消费者组,只要 group.id 相同,就属于同一个消费者组
     props.put("group.id", "test");
     props.put("enable.auto.commit", "false");//关闭自动提交 offset
     props.put("key.deserializer", 
    "org.apache.kafka.common.serialization.StringDeserializer");
     props.put("value.deserializer", 
    "org.apache.kafka.common.serialization.StringDeserializer");
     KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
     consumer.subscribe(Arrays.asList("first"));//消费者订阅主题
     while (true) {
        //消费者拉取数据
         ConsumerRecords<String, String> records = consumer.poll(100);
         for (ConsumerRecord<String, String> record : records) {
             System.out.printf("offset = %d, key = %s, value 
                = %s%n", record.offset(), record.key(), record.value());
         }
        //同步提交,当前线程会阻塞直到 offset 提交成功
         consumer.commitSync();
     }
}
2 )异步提交 offset
虽然同步提交 offset 更可靠一些,但是由于其会阻塞当前线程,直到提交成功。因此吞
吐量会收到很大的影响。因此更多的情况下,会选用异步提交 offset 的方式。
public static void main(String[] args) {
     Properties props = new Properties();
     //Kafka 集群
     props.put("bootstrap.servers", "hadoop102:9092");
     //消费者组,只要 group.id 相同,就属于同一个消费者组
     props.put("group.id", "test");
    //关闭自动提交 offset
     props.put("enable.auto.commit", "false");
     props.put("key.deserializer", 
    "org.apache.kafka.common.serialization.StringDeserializer");
     props.put("value.deserializer", 
    "org.apache.kafka.common.serialization.StringDeserializer");
     KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
     consumer.subscribe(Arrays.asList("first"));//消费者订阅主题
     while (true) {
         //消费者拉取数据
         ConsumerRecords<String, String> records = consumer.poll(100);
         for (ConsumerRecord<String, String> record : records) {
             System.out.printf("offset = %d, key = %s, value 
                = %s%n", record.offset(), record.key(), record.value());
         }
    //异步提交
     consumer.commitAsync(new OffsetCommitCallback() {
         @Override
         public void onComplete(Map<TopicPartition, 
            OffsetAndMetadata> offsets, Exception exception) {
             if (exception != null) {
                System.err.println("Commit failed for" + offsets);
             }
         }
     });
     }
}

 三、自定义 Interceptor

        对于 producer 而言, interceptor 使得用户在消息发送前以及 producer 回调逻辑前有机会对消息做一些定制化需求,比如 修改消息 等。同时, producer 允许用户指定多个 interceptor 按序作用于同一条消息从而形成一个拦截链(interceptor chain)。其定义的方法包括:     
1 configure(configs)
         获取配置信息和初始化数据时调用。
(2)onSend(ProducerRecord):
        该方法封装进 KafkaProducer.send 方法中,即它运行在用户主线程中。用户可以在该方法中对消息做任何操作,但最好保证不要修改消息所属的 topic 和分区,否则会影响目标分区的计算。
(3)onAcknowledgement(RecordMetadata, Exception):
        该方法会在消息从 RecordAccumulator 成功发送到 Kafka Broker 之后,或者在发送过程中失败时调用。
(4)close:
        关闭 interceptor,主要用于执行一些资源清理工作
        

1、案例

        实现一个简单的双 interceptor 组成的拦截链。第一个 interceptor 会在消息发送前将时间 戳信息加到消息 value 的最前部;第二个 interceptor 会在消息发送后更新成功发送消息数或失败发送消息数。

1)增加时间戳拦截器 

public class TimeInterceptor implements ProducerInterceptor<String, String> {
    @Override
    public void configure(Map<String, ?> configs) {
    }

    @Override
    public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {
        // 创建一个新的 record,把时间戳写入消息体的最前部
        return new ProducerRecord(record.topic(), 
          record.partition(), record.timestamp(), record.key(),
          System.currentTimeMillis() + "," + 
          record.value().toString());
    }

    @Override
    public void onAcknowledgement(RecordMetadata metadata, 
        Exception exception) {
    }

    @Override
    public void close() {
    } 
}

(2)增加次数拦截器 

 统计发送消息成功和发送失败消息数,并在 producer 关闭时打印这两个计数器

public class CounterInterceptor implements ProducerInterceptor<String, String>{
     private int errorCounter = 0;
     private int successCounter = 0;
    @Override
    public void configure(Map<String, ?> configs) {
    }

    @Override
    public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {
        return record;
    }
    
    @Override
    public void onAcknowledgement(RecordMetadata metadata, Exception exception) {
        // 统计成功和失败的次数
         if (exception == null) {
             successCounter++;
         } else {
             errorCounter++;
         } 
    }

    @Override
    public void close() {
         // 保存结果
         System.out.println("Successful sent: " + successCounter);
         System.out.println("Failed sent: " + errorCounter);
    } 
}

 3producer 主程序

public static void main(String[] args) throws Exception {
    // 1 设置配置信息
    Properties props = new Properties();
    props.put("bootstrap.servers", "hadoop102:9092");
    props.put("acks", "all");
    props.put("retries", 3);
    props.put("batch.size", 16384);
    props.put("linger.ms", 1);
    props.put("buffer.memory", 33554432);
    props.put("key.serializer", 
        "org.apache.kafka.common.serialization.StringSerializer");
    props.put("value.serializer", 
        "org.apache.kafka.common.serialization.StringSerializer");
    // 2 构建拦截链
    List<String> interceptors = new ArrayList<>();
    interceptors.add("com.atguigu.kafka.interceptor.TimeInterceptor"); 
    interceptors.add("com.atguigu.kafka.interceptor.CounterInterceptor"); 
    props.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, interceptors);
    String topic = "first";
    Producer<String, String> producer = new KafkaProducer<>(props);
    // 3 发送消息
    for (int i = 0; i < 10; i++) {
         ProducerRecord<String, String> record = 
            new ProducerRecord<>(topic, "message" + i);
     producer.send(record);
    }
    // 4 一定要关闭 producer,这样才会调用 interceptor 的 close 方法
    producer.close();
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值