Kafka中如何创建消费者Consumer已经在前面给大家详细的讲解过,那么如何使用JAVA来消费topic中的数据呢呢,今天就说说。
还是先创建一个topic,拥有一个副本和一个分区
kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test
示例:
public class MyConsumer{
public static void main(String[] args) {
Properties prop = new Properties();
prop.put("bootstrap.servers", "node01:9092,node02:9092,node03:9092");
//或者
prop.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.5.150:9092");
prop.put("group.id", "testGroup1"); //必须要组名
//或者
prop.put(ConsumerConfig.GROUP_ID_CONFIG,"testGroup1");
prop.put("enable.auto.commit", "true");//默认值true 指定为自动提交offset
//或者
prop.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true");
prop.put("auto.commit.interval.ms", "1000");//默认值5000
//或者
prop.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");
prop.put(auto.offset.reset, "earliest"); //相当于从开始读 --from-beginning
//或者
prop.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
//反序列化
prop.put("key.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
//或者
prop.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringDeserializer");
prop.put("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
//或者
prop.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringDeserializer");
//创建consumer对象
KafkaConsumer<String, String> consumer = new KafkaConsumer(prop);
//消费者订阅的topic, 可同时订阅多个,指定topic名字
consumer.subscribe(Arrays.asList("mytest"));
//从topic消费数据
consumer.assign(Arrays.asList(new TopicPartition("mytest",0))); //分区号是0分区,就是第一个分区
while (true) {
ConsumerRecords<String, String> records = consumer.poll(1000); //按时间段读取数据,读取超时时间为1000ms
for (ConsumerRecord<String, String> record : records){
System.out.printf(
record.partition(),record.offset(), record.key(), record.value());
}
}
}
}
解析:
如果consumer在获得数据后需要加入处理,数据完毕后才确认offset,需要程序来控制offset的确认。举个例子:
consumer获得数据后,需要将数据持久化到DB中。自动确认offset的情况下,如果数据从kafka集群读出,就确认,但是持久化过程失败,就会导致数据丢失。我们就需要控制offset的确认。
Properties props = new Properties();
props.put("bootstrap.servers", "node01:9092");
props.put("group.id", "testGroup1"); //必须要组名
//指定为手动提交offset
props.put("enable.auto.commit", "false");//默认值true
props.put("auto.commit.interval.ms", "1000");
props.put("session.timeout.ms", "30000");
props.put("key.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
//创建consumer对象
KafkaConsumer<String, String> consumer = new KafkaConsumer(prop);
//消费者订阅的topic, 可同时订阅多个,指定topic名字
consumer.subscribe(Arrays.asList("mytest"));
final int minBatchSize = 200;
List<ConsumerRecord<String, String>> buffer = new ArrayList<>();
while (true) {
ConsumerRecords<String, String> records = consumer.poll(1000);
for (ConsumerRecord<String, String> record : records) {
buffer.add(record);
}
//数据达到批量要求,就写入DB,同步确认offset
if (buffer.size() >= minBatchSize) {
//提交offset,只要是未提交,表示消息没有被消费,下次重启的时候会继续消费
insertIntoDb(buffer);
consumer.commitAsync();
buffer.clear();
}
}
//创建consumer对象
KafkaConsumer<String, String> consumer = new KafkaConsumer(props);
consumer.subscribe(Arrays.asList("mytest")); //指定topic名字
//从topic消费数据
consumer.assign(Arrays.asList(new TopicPartition("mytest",0))); //分区号是0分区,就是第一个分区
//消费2号分区的数据
consumer.seek(testpartition2,offset:50)//指定从topic的2号分区的某个offset开始消费
//从头开始消费
consumer.seekToBeginning(Arrays.asList(testpartition2)
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Long.MAX_VALUE);
for (ConsumerRecord<String, String> record : records)
System.out.printf(
"partition=%d,offset=%d,key=%s,value=%s%n",
record.partition(),record.offset(),record.key(), record.value())
}
}
说明:确认的offset为已接受数据最大offset+1。
可以向特定的分区订阅消息。但是会失去partion的负载分担。有几种场景可能会这么用:
String topic = "mytest"
TopicPartition partition0 = new TopicPartition(topic, 0);
TopicPartition partition1 = new TopicPartition(topic, 1);
consumer.assign(Arrays.asList(partition0, partition1));
说明:
消费者可以自定义kafka的offset存储位置。该设计的主要目的是让消费者将数据和offset进行原子性的存储。这样可以避免上面提到的重复消费问题。举例说明:
订阅特定分区。存储所获得的记录时,将每条记录的offset一起存储。保证数据和offset的存储是原子性的。当异步存储被异常打断时,凡已经存储的数据,都有相应的offset记录。这种方式可以保证不会有数据丢失,也不会重复的从服务端读取。
参数配置:
该情况下:
大多数情况下,服务端的Consumer的消费位置都是由客户端间歇性的确认。Kafka允许Consumer自己设置消费起点,达到的效果:
1.可以消费已经消费过的数据;
2. 可以跳跃性的消费数据;
看下这样做的一些场景:
如果一个consumer同时消费多个分区,默认情况下,这多个分区的优先级是一样的,同时消费。Kafka提供机制,可以让暂停某些分区的消费,先获取其他分区的内容。场景举例:
参数解析: