一、按照topic和分区消费的划分:按照topic消费与按照topic分区消费
1、指定多主题消费
consumer.subscribe(Arrays.asList("t4","t5"));
2、指定分区消费
consumer.assign(list);
3、手动修改偏移量
consumer.commitSync(); //提交当前消费偏移量
consumer.commitSync(Map<TopicPartition, OffsetAndMetadata>) //提交指定偏移量
consumer.assign(Arrays.asList(tp));
4、seek,修改偏移量搜索指针,顺序读取数据
consumer.assign(Arrays.asList(tp));
consumer.seek(tp,0);
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
import org.apache.kafka.common.TopicPartition;
import java.util.*;
public class NewConsumer {
public static void main(String[] args) {
Properties props = new Properties();
props.put("bootstrap.servers","s102:9092,s103:9092,s104:9092");
props.put("group.id", "g3");
props.put("enable.auto.commit", "false");
props.put("auto.commit.interval.ms", "100");
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<String, String>(props);
//通过subscribe方法,指定多主题消费
//consumer.subscribe(Arrays.asList("t4","t5"));
//指定分区消费
// ArrayList<TopicPartition> list = new ArrayList<TopicPartition>();
// TopicPartition tp = new TopicPartition("t1", 0);
// TopicPartition tp2 = new TopicPartition("t4", 0);
// TopicPartition tp3 = new TopicPartition("t4", 1);
// TopicPartition tp4 = new TopicPartition("t4", 2);
// list.add(tp);
// list.add(tp2);
// list.add(tp3);
// list.add(tp4);
// consumer.assign(list);
Map<TopicPartition, OffsetAndMetadata> offset = new HashMap<TopicPartition, OffsetAndMetadata>();
//指定分区
TopicPartition tp = new TopicPartition("t4", 0);
//指定偏移量
OffsetAndMetadata metadata = new OffsetAndMetadata(3);
offset.put(tp,metadata);
//修改偏移量
consumer.commitSync(offset);
//订阅主题
//consumer.subscribe(Arrays.asList("t4"));
consumer.assign(Arrays.asList(tp));
//指定分区
// TopicPartition tp = new TopicPartition("t4", 0);
// consumer.assign(Arrays.asList(tp));
// //修改搜索指针
// consumer.seek(tp,10);
while (true) {
ConsumerRecords<String, String> records = consumer.poll(5000);
for (ConsumerRecord<String, String> record : records)
System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
consumer.commitSync();
}
}
}
二、按照获取偏移量的方式划分为:按照topic消费和按照时间戳消费
1.按照topic消费
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "test");
props.put("client.id", "test");
props.put("fetch.max.bytes", 1024);// 为了便于测试,这里设置一次fetch 请求取得的数据最大值为1KB,默认是5MB
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("test"));
try {
int minCommitSize = 10;// 最少处理10 条消息后才进行提交
int icount = 0 ;// 消息计算器
while (true) {
// 等待拉取消息
ConsumerRecords<String, String> records = consumer.poll(1000);
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());
icount++;
}
// 在业务逻辑处理成功后提交偏移量
if (icount >= minCommitSize){
consumer.commitAsync(new OffsetCommitCallback() {
@Override
public void onComplete(Map<TopicPartition, OffsetAndMetadata> offsets, Exception exception) {
if (null == exception) {
// TODO 表示偏移量成功提交
System.out.println("提交成功");
} else {
// TODO 表示提交偏移量发生了异常,根据业务进行相关处理
System.out.println("发生了异常");
}
}
});
icount=0; // 重置计数器
}
}
} catch(Exception e){
// TODO 异常处理
e.printStackTrace();
} finally {
consumer.close();
}
2.按照时间戳消费
Kafka 在0.10.1.1 版本增加了时间戳索引文件,因此我们除了直接根据偏移量索引文件查询消息之外,还可以根据时间戳来访问消息。consumer-API 提供了一个offsetsForTimes(Map<TopicPartition, Long> timestampsToSearch)方法,该方法入参为一个Map 对象,Key 为待查询的分区,Value 为待查询的时间戳,该方法会返回时间戳大于等于待查询时间的第一条消息对应的偏移量和时间戳。需要注意的是,若待查询的分区不存在,则该方法会被一直阻塞。
假设我们希望从某个时间段开始消费,那们就可以用offsetsForTimes()方法定位到离这个时间最近的第一条消息的偏移量,在查到偏移量之后调用seek(TopicPartition partition, long offset)方法将消费偏移量重置到所查询的偏移量位置,然后调用poll()方法长轮询拉取消息。例如,我们希望从主题“stock-quotation”第0 分区距离当前时间相差12 小时之前的位置开始拉取消息
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "test");
props.put("client.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.assign(Arrays.asList(new TopicPartition("test", 0)));
try {
Map<TopicPartition, Long> timestampsToSearch = new HashMap<TopicPartition,Long>();
// 构造待查询的分区
TopicPartition partition = new TopicPartition("stock-quotation", 0);
// 设置查询12 小时之前消息的偏移量
timestampsToSearch.put(partition, (System.currentTimeMillis() - 12 * 3600 * 1000));
// 会返回时间大于等于查找时间的第一个偏移量
Map<TopicPartition, OffsetAndTimestamp> offsetMap = consumer.offsetsForTimes (timestampsToSearch);
OffsetAndTimestamp offsetTimestamp = null;
// 这里依然用for 轮询,当然由于本例是查询的一个分区,因此也可以用if 处理
for (Map.Entry<TopicPartition, OffsetAndTimestamp> entry : offsetMap.entrySet()) {
// 若查询时间大于时间戳索引文件中最大记录索引时间,
// 此时value 为空,即待查询时间点之后没有新消息生成
offsetTimestamp = entry.getValue();
if (null != offsetTimestamp) {
// 重置消费起始偏移量
consumer.seek(partition, entry.getValue().offset());
}
}
while (true) {
// 等待拉取消息
ConsumerRecords<String, String> records = consumer.poll(1000);
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());
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
consumer.close();
}
3.消费速度控制
提供 pause(Collection<TopicPartition> partitions)和resume(Collection<TopicPartition>
partitions)方法,分别用来暂停某些分区在拉取操作时返回数据给客户端和恢复某些分区向客户端返回数据操作。通过这两个方法可以对消费速度加以控制,结合业务使用。