【Kafka】Consumer API
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的更多相关文章
- 【Kafka】Producer API
Producer API Kafka官网文档给了基本格式 地址:http://kafka.apachecn.org/10/javadoc/index.html?org/apache/kafka/cli ...
- 【Kafka】Consumer配置
从0.9.0.0开始,下面是消费者的配置. 名称 描述 类型 默认值 bootstrap.servers 消费者初始连接kafka集群时的地址列表.不管这边配置的什么地址,消费者会使用所有的kafka ...
- 【Kafka】Stream API
Stream API Kafka官方文档给了基本格式 http://kafka.apachecn.org/10/javadoc/index.html?org/apache/kafka/streams/ ...
- 【Kafka】JavaAPI操作
目录 先创建Maven工程导入jar包 Producer API Consumer API Stream API 先创建Maven工程导入jar包 帮助文档地址:http://kafka.apache ...
- 【译】Android API 规范
[译]Android API 规范 译者按: 修改R代码遇到Lint tool的报错,搜到了这篇文档,aosp仓库地址:Android API Guidelines. 58e9b5f Project ...
- 【kafka】Java连接出现Connection refused: no further information的解决方法
在Linux机器(ip:10.102.16.203)安装完kafka(参考:kafka的安装及使用),在windows上使用Java接口访问服务时(参考:Java实现Kafka的生产者.消费者),报异 ...
- 【Kafka】《Kafka权威指南》——分区partition
在上篇的例子里([Kafka]<Kafka权威指南>--写数据), ProducerRecord 对象包含了目标主题.键和值. Kafka 的消息是 一个个 键值对, ProducerRe ...
- 【Kafka】数据分区策略
数据分区策略 四种策略 一.指定分区号,数据会直接发送到所指定的分区 二.没有指定分区号,指定了数据的key,可以通过key获取hashCode决定数据发送到哪个分区 三.都没有指定的话,会采取rou ...
- 【Kafka】Kafka-分区数-备份数-如何设置-怎么确定-怎么修改
Kafka-分区数-备份数-如何设置-怎么确定-怎么修改 kafka partition 数量 更新_百度搜索 kafka重新分配partition - - CSDN博客 如何为Kafka集群选择合适 ...
随机推荐
- 使用SVG内置API计算图形或点经过transform之后的新坐标
一个应用场景是,点击一条路径,显示该路径的控制点.因为有transform变形( 平移.缩放.倾斜.旋转等变换),所以获取变形后的新坐标需要计算. 纯数学的方法,就是用2D变换矩阵的一些公式去运算,过 ...
- spring源码阅读笔记08:bean加载之创建bean
上文从整体视角分析了bean创建的流程,分析了Spring在bean创建之前所做的一些准备工作,并且简单分析了一下bean创建的过程,接下来就要详细分析bean创建的各个流程了,这是一个比较复杂的过程 ...
- 数据结构之栈—强大的四则复杂运算计算器(超过windows自带的科学计算器)【中缀转后缀表达式】
比windows自带计算器还强的四则复杂运算计算器! 实测随机打出两组复杂算式:-7.5 * 6 / ( -2 + ( -6.5 - -5.22 ) )与7.5+-3*8/(7+2) windows ...
- CVE 2019-0708 漏洞复现+
PART 1 参考链接:https://blog.csdn.net/qq_42184699/article/details/90754333 漏洞介绍: 当未经身份验证的攻击者使用 RDP 连接到目标 ...
- [javascript]JS获取当前时间戳的方法
JavaScript 获取当前时间戳: 第一种方法:(这种方法只精确到秒) var timestamp = Date.parse(new Date()); 结果:1280977330000 第二种方法 ...
- 【arithmetic】搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引.如果目标值不存在于数组中,返回它将会被按顺序插入的位置 可以假设数组中无重复元素. 示例 1: 输入: [1,3,5,6], 5 输出: ...
- Java中基础类基础方法(学生类)(手机类)
学生类: //这是我的学生类class Student { //定义变量 //姓名 String name; //null //年龄 int age; //0 //地址 String address; ...
- javascript-数组简单的认识
一起组团(什么是数组) 我们知道变量用来存储数据,一个变量只能存储一个内容.假设你想存储10个人的姓名或者存储20个人的数学成绩,就需要10个或20个变量来存储,如果需要存储更多数据,那就会变的更麻烦 ...
- JavaScript type="text/template"的用法
JavaScript type="text/template"相当于定义一个模板,如果没有使用html()方法的话,是显示不出来的,我们直接看例子(我是在tp框架的里面写的) &l ...
- Kubernetes产生背景、核心概念
Kubernetes是什么 • Kubernetes是Google在2014年开源的一个容器集群管理系统,Kubernetes简称K8S. • Kubernetes用于容器化应用程序的部署,扩展和管理 ...