Consumer API


Kafka官网文档给了基本格式

http://kafka.apachecn.org/10/javadoc/index.html?org/apache/kafka/clients/consumer/KafkaConsumer.html

JavaAPI 模板

自动提交offset

Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.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.subscribe(Arrays.asList("foo", "bar"));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(100);
for (ConsumerRecord<String, String> record : records)
System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
}

手动提交offset

Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "test");
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("foo", "bar"));
final int minBatchSize = 200;
List<ConsumerRecord<String, String>> buffer = new ArrayList<>();
while (true) {
ConsumerRecords<String, String> records = consumer.poll(100);
for (ConsumerRecord<String, String> record : records) {
buffer.add(record);
}
if (buffer.size() >= minBatchSize) {
insertIntoDb(buffer);
consumer.commitSync();
buffer.clear();
}
}

自定义 自动提交offset

在这之前需要明白一点,自动提交是有可能造成重复消费的

比如我们设置的props.put("auto.commit.interval.ms", "1000");——提交offset值的时间间隔为1s

现在有这么几条数据等待消费


157 hello offset

…

287 hello world

295 abc test 900ms

351 hello abc 1000ms


157 hello offset 为这一次提交offset值的起点,351 hello abc 为提交offset值的重点

295 abc test 是到900ms的时候提交的offset,如果在此时发生了宕机,重新开始就会从157 hello offset再次进行消费,就会造成重复消费的情况

package cn.itcast.kafka.demo2;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer; import java.util.Arrays;
import java.util.Properties; public class MyConsumer {
public static void main(String[] args) {
Properties props = new Properties();
//指定Kafka服务器地址
props.put("bootstrap.servers", "node01:9092,node02:9092,node03:9092");
//指定消费者组的名字
props.put("group.id", "testGroup");
//允许程序自动提交offset,保存到kafka当中的一个topic中去
props.put("enable.auto.commit", "true");
//每隔多长时间提交一次offset的值
props.put("auto.commit.interval.ms", "1000");
//数据key和value的序列化
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
//定义KafkaConsumer
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
//订阅topic:test,并消费其中的数据
consumer.subscribe(Arrays.asList("test"));
//死循环拉取数据
while (true) {
//所有拉取到的数据都封装在了ConsumerRecords
ConsumerRecords<String, String> records = consumer.poll(1000);
for (ConsumerRecord<String, String> record : records) {
int partition = record.partition();
String value = record.value();
long offset = record.offset();
String key = record.key();
System.out.printf("数据的key为" + key + ",数据的value为" + value + ",数据的offset为" + offset + ",数据的分区为" + partition);
} }
}
}

自定义 手动提交offset
package cn.itcast.kafka.demo2;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties; public class ManualOffsetCommit {
public static void main(String[] args) {
Properties props = new Properties();
props.put("bootstrap.servers", "node01:9092,node02:9092,node03:9092");
props.put("group.id", "testGroup2");
//关闭自动提交offset值,改为手动提交
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"));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(1000);
for (ConsumerRecord<String, String> record : records) {
int partition = record.partition();
String value = record.value();
long offset = record.offset();
String key = record.key();
System.out.printf("数据的key为" + key + ",数据的value为" + value + ",数据的offset为" + offset + ",数据的分区为" + partition);
}
// ConsumerRecords 里面的数据消费完后,需要提交offset值
// 使用异步提交的方法,不会阻塞程序的消费
// consumer.commitSync();
// 同步提交
consumer.commitSync();
}
}
}

消费完每个分区后手动提交offset
package cn.itcast.kafka.demo2;

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 CommitPartition {
public static void main(String[] args) {
Properties props = new Properties();
//指定kafka服务器地址
props.put("bootstrap.servers", "node01:9092,node02:9092,node03:9092");
//指定消费者组的名字
props.put("group.id", "testGroup4");
//关闭自动提交offset值,改为手动提交
props.put("enable.auto.commit", "false");
//数据key和value的序列化
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
//定义kafkaConsumer
KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<>(props);
//订阅topic:test 并消费其中的数据
kafkaConsumer.subscribe(Arrays.asList("test"));
//调用poll方法,获取所有的数据,包含各个分区的数据
ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(3000);
//获取topic中所有分区
Set<TopicPartition> partitions = consumerRecords.partitions();
//循环消费数据
for (TopicPartition topicPartition : partitions) {
//获取一个分区立面的所有数据
List<ConsumerRecord<String, String>> records = consumerRecords.records(topicPartition);
for (ConsumerRecord<String, String> record : records) {
int partition = record.partition();
String value = record.value();
String key = record.key();
long offset = record.offset();
System.out.println("数据的key为" + key + ",数据的value为" + value + ",数据的offset为" + offset + ",数据的分区为" + partition);
}
//提交partition的offset值
//Map<TopicPartition, OffsetAndMetadata> offsets //获取分区里面最后一条数据的offset值
long offset = records.get(records.size() - 1).offset();
Map<TopicPartition, OffsetAndMetadata> topicPartitionOffsetAndMetadataMap = Collections.singletonMap(topicPartition, new OffsetAndMetadata(offset)); //处理完成一个分区里面的数据后提交offset
kafkaConsumer.commitSync(topicPartitionOffsetAndMetadataMap);
} }
}

消费指定分区数据
package cn.itcast.kafka.demo2;

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 ConsumerMyPartition {
public static void main(String[] args) {
Properties props = new Properties();
props.put("bootstrap.servers", "node01:9092,node02:9092,node03:9092");
//指定消费者组的名字
props.put("group.id", "testGroup4");
//关闭自动提交offset值,改为手动提交
props.put("enable.auto.commit", "false");
//数据key和value的序列化
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
//定义kafkaComsumer
KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<>(props); //Collection<TopicPartition> partitions
//创建一个集合 泛型为TopicPartition
TopicPartition topicPartition = new TopicPartition("test", 0);
TopicPartition topicPartition1 = new TopicPartition("test", 1); List<TopicPartition> topicPartitions = Arrays.asList(topicPartition, topicPartition1); //通过assign方法注册消费topic:test中的某些分区
kafkaConsumer.assign(topicPartitions); while (true) {
ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(3000);
//获取所有分区
Set<TopicPartition> partitions = consumerRecords.partitions();
for (TopicPartition topicPartition2 : partitions) {
//获取一个分区中的所有数据
List<ConsumerRecord<String, String>> records = consumerRecords.records(topicPartition2);
for (ConsumerRecord<String, String> record : records) {
int partition = record.partition();
String value = record.value();
String key = record.key();
long offset = record.offset();
System.out.println("数据的key为" + key + ",数据的value为" + value + ",数据的offset为" + offset + ",数据的分区为" + partition);
}
long offset = records.get(records.size() - 1).offset();
kafkaConsumer.commitSync(Collections.singletonMap(topicPartition2, new OffsetAndMetadata(offset))); }
}
}
}

重复消费和数据丢失



以上图为例,Consumer需要将数据写入到Hbase后,再提交offset值。那么就可以有四种上传情况的发生:

一、写入Hbase成功,提交offset成功 —— 这就是正常的消费情况

二、写入Hbase失败,提交offset失败 —— 不会有什么影响,继续进行消费即可

三、写入Hbase成功,但是offset提交失败 —— 这就会造成重复消费

四、写入Hbase失败,但是offset提交成功 —— 这样就会造成数据丢失



Kafka一共有三种消费模型:

exactly once —— 没有出错

at least once —— 重复消费

at most once —— 数据丢失

出现后两种模型的原因一般是offset没有管理好

实际工作中大多数公司的解决办法是将offset的值保存到redis或者hbase当中



数据消费存在高阶API (High Level API) 和低阶API (High Level API)。

高阶API是将offset值默认保存在zk中,早期的Kafka一般默认使用高阶API。

低阶API就是将offset值保存在kafka自带的一个topic种

【Kafka】Consumer API的更多相关文章

  1. 【Kafka】Producer API

    Producer API Kafka官网文档给了基本格式 地址:http://kafka.apachecn.org/10/javadoc/index.html?org/apache/kafka/cli ...

  2. 【Kafka】Consumer配置

    从0.9.0.0开始,下面是消费者的配置. 名称 描述 类型 默认值 bootstrap.servers 消费者初始连接kafka集群时的地址列表.不管这边配置的什么地址,消费者会使用所有的kafka ...

  3. 【Kafka】Stream API

    Stream API Kafka官方文档给了基本格式 http://kafka.apachecn.org/10/javadoc/index.html?org/apache/kafka/streams/ ...

  4. 【Kafka】JavaAPI操作

    目录 先创建Maven工程导入jar包 Producer API Consumer API Stream API 先创建Maven工程导入jar包 帮助文档地址:http://kafka.apache ...

  5. 【译】Android API 规范

    [译]Android API 规范 译者按: 修改R代码遇到Lint tool的报错,搜到了这篇文档,aosp仓库地址:Android API Guidelines. 58e9b5f Project ...

  6. 【kafka】Java连接出现Connection refused: no further information的解决方法

    在Linux机器(ip:10.102.16.203)安装完kafka(参考:kafka的安装及使用),在windows上使用Java接口访问服务时(参考:Java实现Kafka的生产者.消费者),报异 ...

  7. 【Kafka】《Kafka权威指南》——分区partition

    在上篇的例子里([Kafka]<Kafka权威指南>--写数据), ProducerRecord 对象包含了目标主题.键和值. Kafka 的消息是 一个个 键值对, ProducerRe ...

  8. 【Kafka】数据分区策略

    数据分区策略 四种策略 一.指定分区号,数据会直接发送到所指定的分区 二.没有指定分区号,指定了数据的key,可以通过key获取hashCode决定数据发送到哪个分区 三.都没有指定的话,会采取rou ...

  9. 【Kafka】Kafka-分区数-备份数-如何设置-怎么确定-怎么修改

    Kafka-分区数-备份数-如何设置-怎么确定-怎么修改 kafka partition 数量 更新_百度搜索 kafka重新分配partition - - CSDN博客 如何为Kafka集群选择合适 ...

随机推荐

  1. 2020不平凡的90天,Python分析三个月微博热搜数据带你回顾

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:刘早起早起 PS:如有需要Python学习资料的小伙伴可以加点击下方链 ...

  2. F. 蚂蚁装修

    单点时限: 2.0 sec 内存限制: 512 MB 还有一个月就开学了,爱学习的小蚂蚁想庆祝一下!于是它要把它的“家”装修一下.首先要做的就是贴地板.小蚂蚁“家”的地面可以看成一个2∗N 的方格 , ...

  3. 2. js的异步

    1. 回掉2. promise3. Generator4. Async/await

  4. MySQL服务端恶意读取客户端文件漏洞 (DDCTF2019和国赛均涉及到这个漏洞)

    mysql协议中流程和go语言实现的恶意mysql服务器:https://blog.csdn.net/ls1120704214/article/details/88174003 poc :https: ...

  5. springmvc配置数据源方式

    1 阿里巴巴的druid数据源 <!-- 配置数据源 使用的是Druid数据源 -->-<bean destroy-method="close" init-met ...

  6. C/C++,被誉为“最经典的编程语言”,不仅是因为编程入门需要学?

    计算机诞生初期,用机器语言或汇编语言编写程序; 第一种高级语言FORTRAN诞生于1954年; BASIC语言(1964)是由FORTRAN语言的简化而成的是为初学者设计的小型高级语言; C语言是19 ...

  7. ubuntu(Linux)下,查看CPU性能集合

    CPU: 详细:cat /proc/cpuinfo型号:grep "model name" /proc/cpuinfo |awk -F ':' '{print $NF}'数量:ls ...

  8. Idea上tomcat部署细节

    ​ 一.On Update action: (1)Update resources:更新项目变更的.jsp,.xml文件等资源文件,而不会更新源码文件:(仅修改项目的JS文件.JSP文件.CSS文件推 ...

  9. 在项目中部署redis的读写分离架构(包含节点间认证口令)

    #### 在项目中部署redis的读写分离架构(包含节点间认证口令) ##### 1.配置过程 ---  1.此前就是已经将redis在系统中已经安装好了,redis utils目录下,有个redis ...

  10. phpspider框架的使用

    手册:https://doc.phpspider.org/configs-members.html 参考:https://www.jianshu.com/p/01052508ea7c 不多说,代码贴上 ...