本节目录
- 入门程序
- 消费日志topic
- 滑动窗口统计消费topic
1 入门程序
public class ConsumerDemo {
public static void main(String[] args) {
Properties props = new Properties();
props.setProperty("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.setProperty("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.setProperty("bootstrap.servers", "h1:9092,h2:9092,h3:9092");
// groupid是消费者所属的消费组的标识 ,同一个组中的消费者在消费一个topic数据时,会各自消费一部分,互不重复
props.setProperty("group.id", "g1");
// 消费起始偏移量自动指定,默认值是latest
props.setProperty("auto.offset.reset", "latest");
// 是否要把消费者消费到的offset自动提交到kafka去保存 :__consumer_offsets
/***
* 消费者组在消费数据的时候,可以讲当前消费到的offset位置,自动记录到kafka中一个特别的topic中: __consumer_offsets
* 这样一来,万一在某一瞬间,消费者所在机器宕机,崩溃
* 那么,将消费者重启后,它能从kafka的__consumer_offsets中找到崩溃前消费到的位置,从而可以从那个地方继续往后消费,避免重复消费
*/
props.setProperty("enable.auto.commit", "true");
// 构造一个kafka消费者客户端
KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(props);
ArrayList<String> topics = new ArrayList<>();
topics.add("doit14-2");
// 消费者在消费数据之前,需要先“订阅”主题
consumer.subscribe(topics);
while (true) {
// ConsumerRecords是一个Iterable,里面有一个迭代器,所以,取数据时可以拿出迭代器iterator来迭代,也可以直接用增强for循环迭代
ConsumerRecords<String, String> records = consumer.poll(200);
/*Iterator<ConsumerRecord<String, String>> iter = records.iterator();
while(iter.hasNext()){
ConsumerRecord<String, String> rec = iter.next();
String key = rec.key();
String value = rec.value();
}*/
for (ConsumerRecord<String, String> rec : records) {
String key = rec.key();
String value = rec.value();
System.out.println("key: " + key + "\t value: " + value);
}
}
}
}
2 消费日志topic
/**
* --多易教育
* web用户行为日志数据统计:行为事件的发生次数
*/
public class LogEventCalc {
public static void main(String[] args) {
Properties = new Properties();
p.setProperty("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
p.setProperty("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
p.setProperty("bootstrap.servers", "h1:9092,h2:9092,h3:9092");
p.setProperty("group.id", "g1");
p.setProperty("auto.offset.reset", "latest");
p.setProperty("enable.auto.commit", "true");
// 构造一个kafka消费者客户端
KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(props);
ArrayList<String> topics = new ArrayList<>();
topics.add("doit14-log");
// 消费者在消费数据之前,需要先“订阅”主题
consumer.subscribe(topics);
HashMap<String, Integer> cntMap = new HashMap<>();
while(true){
ConsumerRecords<String, String> records = consumer.poll(100);
for (ConsumerRecord<String, String> record : records) {
String line = record.value();
//解析这一条json数据
JSONObject jsonObject = JSON.parseObject(line);
// 提取行为事件类型
String eventid = jsonObject.getString("eventid");
// 对事件计数
cntMap.put(eventid,cntMap.getOrDefault(eventid,0)+1);
// 打印统计结果
System.out.println(cntMap);
}
}
}
}
3 滑动窗口统计消费topic
/**
* -- 多易教育
* 滑动窗口统计
* 窗口长度:10s
* 滑动距离:5s
*/
public class SlideWindowCalc {
public static void main(String[] args) {
// 构造所需的数据结构
ArrayBlockingQueue<ConsumerRecord> queue = new ArrayBlockingQueue<>(1000);
HashMap<String, Integer> cntMap = new HashMap<>();
// 1. 构造一个线程,来持续不断地从kafka中拉取数据,放入一个阻塞队列
new Thread(new Runnable() {
@Override
public void run() {
// 构造一个kafka消费者客户端
Properties props = new Properties();
props.setProperty("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.setProperty("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.setProperty("bootstrap.servers", "h1:9092,h2:9092,h3:9092");
props.setProperty("auto.offset.reset", "latest");
props.setProperty("group.id","aa");
props.setProperty("enable.auto.commit", "true");
// 构造一个kafka消费者客户端
KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(props);
// 消费者在消费数据之前,需要先“订阅”主题
consumer.subscribe(Arrays.asList("doit14-window"));
while(true){
ConsumerRecords<String, String> records = consumer.poll(100);
for (ConsumerRecord<String, String> record : records) {
try {
// 将kafka消息用阻塞方法插入队列,如果队列已满,会一直等待可用空间的到来
queue.put(record);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
// 2. 构造一个定时任务,每隔5s运行一次,从阻塞队列中取最近10s的数据进行统计
Timer timer = new Timer();
TimerTask task = new TimerTask(){
@Override
public void run() {
long taskStartTs = System.currentTimeMillis();
// 从阻塞队列中取数据,进行统计
//queue.take(); //阻塞方法,如果队列中有数据,就取到数据并返回,如果队列中没有数据,则等待数据的到来
try {
while(true) {
ConsumerRecord<String,String> rec = queue.poll(10, TimeUnit.MILLISECONDS);
if(rec != null && rec.timestamp()>taskStartTs-10000 && rec.timestamp()<taskStartTs ){
String word = rec.value();
cntMap.put(word,cntMap.getOrDefault(word,0)+1);
}else if(rec != null && rec.timestamp()>=taskStartTs){
break;
}else if(System.currentTimeMillis()-taskStartTs > 2500){
break;
}
}
System.out.println("-----------------calc time : " + taskStartTs + " -------------------- ");
System.out.println(cntMap);
// 清空hashmap
cntMap.clear();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
timer.schedule(task,0,5000);
}
}