1、Kafka的消费者提交方式
1)、自动提交,这种方式让消费者来管理位移,应用本身不需要显式操作。当我们将enable.auto.commit设置为true,那么消费者会在poll方法调用后每隔五秒(由auto.commit.interval.ms指定)提交一次位移。和很多其他操作一样,自动提交也是由poll方法来驱动的,在调用poll方法的时候,消费者判断是否到达提交时间,如果是则提交上一次poll返回的最大位移。需要注意的是,这种方式可能会导致消息重复消费,假如,某个消费者poll消息后,应用正在处理消息,在3秒后kafka进行了重平衡,那么由于没有更新位移导致重平衡后这部分消息重复消费。
2)、同步提交。
1 package com.demo.kafka.consumer;
2
3 import java.time.Duration;
4 import java.util.Arrays;
5 import java.util.Collections;
6 import java.util.List;
7 import java.util.Properties;
8 import java.util.regex.Pattern;
9
10 import org.apache.kafka.clients.consumer.ConsumerConfig;
11 import org.apache.kafka.clients.consumer.ConsumerRecord;
12 import org.apache.kafka.clients.consumer.ConsumerRecords;
13 import org.apache.kafka.clients.consumer.KafkaConsumer;
14 import org.apache.kafka.clients.consumer.OffsetAndMetadata;
15 import org.apache.kafka.clients.producer.ProducerConfig;
16 import org.apache.kafka.common.TopicPartition;
17 import org.apache.kafka.common.serialization.StringDeserializer;
18
19 public class KafkaConsumerSimple {
20
21 // 设置服务器地址
22 private static final String bootstrapServer = "192.168.110.142:9092";
23
24 // 设置主题
25 private static final String topic = "topic-demo";
26
27 // 设置主题
28 private static final String topic2 = "topic-demo2";
29
30 // 设置消费者组
31 private static final String groupId = "group.demo";
32
33 public static void main(String[] args) {
34 Properties properties = new Properties();
35 // 设置反序列化key参数信息
36 properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
37 // 设置反序列化value参数信息
38 properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
39
40 // 设置服务器列表信息,必填参数,该参数和生产者相同,,制定链接kafka集群所需的broker地址清单,可以设置一个或者多个
41 properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServer);
42
43 // 设置消费者组信息,消费者隶属的消费组,默认为空,如果设置为空,则会抛出异常,这个参数要设置成具有一定业务含义的名称
44 properties.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
45
46 // 制定kafka消费者对应的客户端id,默认为空,如果不设置kafka消费者会自动生成一个非空字符串。
47 properties.put("client.id", "consumer.client.id.demo");
48
49 // 设置每次从最早的offset开始消费
50 properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
51
52 // 手动提交开启
53 properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
54
55 // 将参数设置到消费者参数中
56 KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(properties);
57
58 // 消息订阅
59 // consumer.subscribe(Collections.singletonList(topic));
60 // 可以订阅多个主题
61 // consumer.subscribe(Arrays.asList(topic, topic2));
62 // 可以使用正则表达式进行订阅
63 // consumer.subscribe(Pattern.compile("topic-demo*"));
64
65 // 指定订阅的分区
66 TopicPartition topicPartition = new TopicPartition(topic, 0);
67 consumer.assign(Arrays.asList(topicPartition));
68
69 // 初始化offset位移为-1
70 long lastConsumeOffset = -1;
71 while (true) {
72 // 每隔一秒监听一次,拉去指定主题分区的消息
73 ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));
74 if (records.isEmpty()) {
75 break;
76 }
77 // 获取到消息
78 List<ConsumerRecord<String, String>> partitionRecords = records.records(topicPartition);
79 // 获取到消息的offset位移信息,最后消费的位移
80 lastConsumeOffset = partitionRecords.get(partitionRecords.size() - 1).offset();
81 // System.out.println("the last offset is " + lastConsumeOffset);
82 // 同步提交消费位移
83 consumer.commitSync();
84 }
85 // 当前消费者最后一个消费的位置
86 System.out.println("consumed offset is " + lastConsumeOffset);
87 // 提交,下次消费从哪个位置开始
88 OffsetAndMetadata committed = consumer.committed(topicPartition);
89 System.out.println("committed offset is " + committed.offset());
90 // 下次消费从哪个位置开始
91 long position = consumer.position(topicPartition);
92 System.out.println("the offset of the next record is " + position);
93
94 }
95
96 }
3)、异步提交方式。手动提交有一个缺点,就是当发起提交时调用应用会阻塞。当然我们可以减少手动提交的频率,但这个会增加消息重复的概率(和自动提交一样)。另外一个解决方法是,使用异步提交。但是异步提交也有一个缺点,那就是如果服务器返回提交失败,异步提交不会进行重试。相比较起来,同步提交会进行重试知道成功或者最后抛出异常给应用。异步提交没有实现重试是因为,如果同时存在多个异步提交,进行重试可能会导致位移覆盖。比如,我们发起一个异步提交commitA,此时提交位移是2000,随后又发起了一个异步提交commitB且位移为3000,commitA提交失败但commitB提交失败,此时commitA进行重试并成功的话,会将实际上已经提交的位移从3000回滚到2000,导致消息重复消费。
1 package com.demo.kafka.consumer;
2
3 import java.time.Duration;
4 import java.util.Arrays;
5 import java.util.Map;
6 import java.util.Properties;
7 import java.u