【kafka】Part3 - JavaAPI的简单使用

回顾


安装&快速开始
详细介绍&工作原理

依赖


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

生产者


简单看下kafka生产者的配置

  • bootstrap.servers:kafka的地址
  • ack:消息的确认机制,默认值是0
    acks=0:如果设置为0,生产者不会等待kafka的响应。
    acks=1:这个配置意味着kafka会把这条消息写到本地日志文件中,但是不会等待集群中其他机器的成功响应。
    acks=all:这个配置意味着leader会等待所有的follower同步完成。这个确保消息不会丢失,除非kafka集群中所有机器挂掉。这是最强的可用性保证。
  • retries:配置为大于0的值的话,客户端会在消息发送失败时重新发送
  • batch.size:当多条消息需要发送到同一个分区时,生产者会尝试合并网络请求。这会提高client和生产者的效率
  • key.serializer: 键序列化,默认org.apache.kafka.common.serialization.StringDeserializer
  • value.deserializer:值序列化,默认org.apache.kafka.common.serialization.StringDeserializer

    更详细的配置见官方文档
    kafka中文文档

异步发送

public class Send {
    public void sendMessage(String topicName) {
        Properties props = new Properties();
        props.put("bootstrap.servers", "127.0.0.1:9092");
        props.put("acks", "all");
        props.put("retries", 0);
        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");
    
        Producer<String, String> producer = new KafkaProducer<>(props);
        for (int i = 0; i < 100; i++) {
            producer.send(new ProducerRecord<String, String>(topicName, Integer.toString(i), "hello kafka " + Integer.toString(i)));
        }
        producer.close();
    }
    public static void main(String[] args) {
        Send demo = new Send();
        demo.sendMessage("my-topic");
    }
}

producer.send(new ProducerRecord<String, String>(topic,key,value));

  • topic:消息队列的名称,可以先行在kafka服务中进行创建。如果kafka中并未创建该topic,那么便会自动创建
  • key:键值,也就是value对应的值,和Map类似
  • value:要发送的数据,数据格式为String类型的

测试

  • 启动zookeeper
  • 启动kafka
  • 运行代码
  • 启动消费者,指定topic名称为“my-topic”
    在这里插入图片描述
    批处理
  • DOS窗口输出:
hello kafka 0
hello kafka 1
...
hello kafka 99

带回调函数的异步发送

public void sendMessageWithCallback(String topicName) {
        Properties props = new Properties();
        props.put("bootstrap.servers", "127.0.0.1:9092");
        props.put("acks", "all");
        props.put("retries", 0);
        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");

        Producer<String, String> producer = new KafkaProducer<>(props);
        for (int i = 0; i < 100; i++) {
            producer.send(new ProducerRecord<String, String>(topicName, Integer.toString(i), "hello kafka " + Integer.toString(i)), (recordMetadata, e) -> {
                if (e == null) {
                    System.out.println("发送成功");
                } else {
                    e.printStackTrace();
                }
            });
        }
        producer.close();
    }

同步发送

同步发送意味着必须等待一个发送消息的ACK后,才可以发送下一条消息

public void sendMessageSynchronize(String topicName) {
    Properties props = new Properties();
    props.put("bootstrap.servers", "127.0.0.1:9092");
    props.put("acks", "all");
    props.put("retries", 0);
    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");

    Producer<String, String> producer = new KafkaProducer<>(props);
    for (int i = 0; i < 100; i++) {
        try {
            RecordMetadata recordMetadata = producer.send(new ProducerRecord<String, String>("my-topic", Integer.toString(i), "hello kafka " + Integer.toString(i))).get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("meta");
    }
    producer.close();
}

消费者


简单看下kafka消费者的配置

  • bootstrap.servers:kafka的地址
  • group.id:组名,不同组名可以重复消费。例如你先使用了组名A消费了kafka的1000条数据,但是你还想再次进行消费这1000条数据,并且不想重新去产生,那么这里你只需要更改组名就可以重复消费了
  • enable.auto.commit:是否自动提交,默认为true
  • auto.commit.interval.ms:从poll(拉)的回话处理时长
  • session.timeout.ms:超时时间
  • max.poll.records:一次最大拉取的条
  • auto.offset.reset:消费规则,默认earliest
    earliest:当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费
    latest:当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据
    none:topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常
  • key.serializer:键序列化,默认org.apache.kafka.common.serialization.StringDeserializer
  • value.deserializer:值序列化,默认org.apache.kafka.common.serialization.StringDeserializer

    更详细的配置见官方文档
    kafka中文文档

自动提交offset(默认)

@SuppressWarnings("all")
public class Recive {
public void reciveMessage() {
        Properties props = new Properties();
        /**
         * 配置可以用ProducerConfig和ConsumerConfig中的常量
         * 里面有相应的DOC说明
         */
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "127.0.0.1:9092");
        props.put(ConsumerConfig.GROUP_ID_CONFIG, "test");
        props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true");
        props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");
        props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000");
        props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 1000);
        props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());

        // Properties props = new Properties();
        // props.put("bootstrap.servers", "127.0.0.1:9092");
        // props.put("group.id", "test");
        // props.put("enable.auto.commit", "true");
        // props.put("auto.commit.interval.ms", "1000");
        // props.put("session.timeout.ms", "30000");
        // props.put("auto.offset.reset", "earliest");
        // props.put("key.deserializer", StringDeserializer.class.getName());
        // props.put("value.deserializer", StringDeserializer.class.getName());

        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        consumer.subscribe(Arrays.asList("my-topic"));
        while (true) {
            ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
            for (ConsumerRecord<String, String> record : records) {
                System.out.printf("topic = %s, offset = %d, key = %s, value = %s%n", record.topic(), record.offset(), record.key(), record.value());
            }
        }
    }
}
public static void main(String[] args) {
    Recive demo = new Recive();
    demo.reciveMessage();
}    

测试

  • 运行消费者代码
  • 运行生产者代码
    控制台打印
topic = my-topic, offset = 800, key = 0, value = hello kafka 0
topic = my-topic, offset = 801, key = 1, value = hello kafka 1
...
topic = my-topic, offset = 899, key = 99, value = hello kafka 99

手动提交offset

offset下标自动提交其实在很多场景都不适用,因为自动提交是在kafka拉取到数据之后就直接提交,这样很容易丢失数据,尤其是在需要事务控制的时候。很多情况下我们需要从kafka成功拉取数据之后,对数据进行相应的处理之后再进行提交。如拉取数据之后进行写入mysql这种,所以这时我们就需要进行手动提交kafka的offset下标。

复习一下offset
offset指的是kafka的topic中的每个消费组消费的下标。简单的来说就是一条消息对应一个offset下标,每次消费数据的时候如果提交offset,那么下次消费就会从提交的offset加一那里开始消费。比如一个topic中有100条数据,我消费了50条并且提交了,那么此时的kafka服务端记录提交的offset就是49(offset从0开始),那么下次消费的时候offset就从50开始消费。
就好比看书,每页都会有一个页码,今天你看了50页,在50页放个书签,明天就从51页开始看。

测试

先测试下不提交

  • props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true");true值改为false
  • 运行消费者代码
  • 跑一次生产者代码
    控制台打印
topic = my-topic, offset = 1000, key = 0, value = hello kafka 0
topic = my-topic, offset = 1001, key = 1, value = hello kafka 1
...
topic = my-topic, offset = 1099, key = 99, value = hello kafka 99
  • 再跑一次生产者代码
    控制台打印
topic = my-topic, offset = 1100, key = 0, value = hello kafka 0
topic = my-topic, offset = 1001, key = 1, value = hello kafka 1
...
topic = my-topic, offset = 1199, key = 99, value = hello kafka 99
  • 重新跑一下消费者代码(注意offset)
    控制台打印
topic = my-topic, offset = 1000, key = 0, value = hello kafka 0
topic = my-topic, offset = 1001, key = 1, value = hello kafka 1
...
topic = my-topic, offset = 1199, key = 99, value = hello kafka 99

可以看到已经重复消费了

手动提交

while (true) {
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
    for (ConsumerRecord<String, String> record : records) {
        System.out.printf("topic = %s, offset = %d, key = %s, value = %s%n", record.topic(), record.offset(), record.key(), record.value());
    }
    // 手动提交
    consumer.commitAsync();
}
  • 自行尝试着测试一下吧!

spring-kafka

Spring-Kafka史上最强入门教程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值