Kafka - 消费接口分析
1.概述
在 Kafka 中,官方对外提供了两种消费 API,一种是高等级消费 API,另一种是低等级的消费 API。在 《高级消费 API》一文中,介绍了其高级消费的 API 实现。今天给大家介绍另一种消费 API。
2.内容
在使用过 Kafka 的高级消费 API 后,我们知道它是一种高度抽象的消费 API,使用起来简单,方便,但是对于某些特殊的需求我们可能要用到第二种更加底层的 API。那么,我们首先需要知道低级消费 API 的作用。它能帮助我们去做那些事情:
- 一个消息进行多次读取
- 在处理过程中只消费 Partition 其中的某一部分消息
- 添加事物管理机制以保证消息仅被处理一次
当然,在使用的过程当中也是有些弊端的,其内容如下:
- 必须在程序中跟踪 Offset 的值
- 必须找出指定的 Topic Partition 中的 Lead Broker
- 必须处理 Broker 的变动
使用其 API 的思路步骤如下所示:
- 从所有处于 Active 状态的 Broker 中找出哪个是指定 Topic Partition 中的 Lead Broker
- 找出指定 Topic Partition 中的所有备份 Broker
- 构造请求
- 发送请求并查询数据
- 处理 Leader Broker 的变动
3.代码实现
3.1 Java Project
若是使用 Java Project 工程去实现该部分代码,需要添加相关以来 JAR 文件,其内容包含如下:
- scala-xml_${version}-${version}.jar
- scala-library-${version}.jar
- metrics-core-${version}.jar
- kafka-client-${version}.jar
- kafka_${version}-${version}.jar
针对 Java Project 工程,需要自己筛选 JAR 去添加。保证代码的顺利执行。
3.2 Maven Project
对 Maven 工程,在 pom.xml 文件中添加相应的依赖信息即可,简单方便。让 Maven 去管理相应的依赖 JAR 文件。内容如下所示:
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka_2.11</artifactId>
<version>0.8.2.1</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
这样在 Maven 工程中相应的依赖 JAR 文件就添加完成了。
3.3 代码实现
在低级消费 API 中,实现代码如下所示:
/**
* @Date Mar 2, 2016
*
* @Author dengjie
*
* @Note Simple consumer api
*/
public class SimpleKafkaConsumer {
private static Logger log = LoggerFactory.getLogger(SimpleKafkaConsumer.class);
private List<String> m_replicaBrokers = new ArrayList<String>(); public SimpleKafkaConsumer() {
m_replicaBrokers = new ArrayList<String>();
} public static void main(String[] args) {
SimpleKafkaConsumer example = new SimpleKafkaConsumer();
// Max read number
long maxReads = SystemConfig.getIntProperty("kafka.read.max");
// To subscribe to the topic
String topic = SystemConfig.getProperty("kafka.topic");
// Find partition
int partition = SystemConfig.getIntProperty("kafka.partition");
// Broker node's ip
List<String> seeds = new ArrayList<String>();
String[] hosts = SystemConfig.getPropertyArray("kafka.server.host", ",");
for (String host : hosts) {
seeds.add(host);
}
int port = SystemConfig.getIntProperty("kafka.server.port");
try {
example.run(maxReads, topic, partition, seeds, port);
} catch (Exception e) {
log.error("Oops:" + e);
e.printStackTrace();
}
} public void run(long a_maxReads, String a_topic, int a_partition, List<String> a_seedBrokers, int a_port)
throws Exception {
// Get point topic partition's meta
PartitionMetadata metadata = findLeader(a_seedBrokers, a_port, a_topic, a_partition);
if (metadata == null) {
log.info("[SimpleKafkaConsumer.run()] - Can't find metadata for Topic and Partition. Exiting");
return;
}
if (metadata.leader() == null) {
log.info("[SimpleKafkaConsumer.run()] - Can't find Leader for Topic and Partition. Exiting");
return;
}
String leadBroker = metadata.leader().host();
String clientName = "Client_" + a_topic + "_" + a_partition; SimpleConsumer consumer = new SimpleConsumer(leadBroker, a_port, 100000, 64 * 1024, clientName);
long readOffset = getLastOffset(consumer, a_topic, a_partition, kafka.api.OffsetRequest.EarliestTime(),
clientName);
int numErrors = 0;
while (a_maxReads > 0) {
if (consumer == null) {
consumer = new SimpleConsumer(leadBroker, a_port, 100000, 64 * 1024, clientName);
}
FetchRequest req = new FetchRequestBuilder().clientId(clientName)
.addFetch(a_topic, a_partition, readOffset, 100000).build();
FetchResponse fetchResponse = consumer.fetch(req); if (fetchResponse.hasError()) {
numErrors++;
// Something went wrong!
short code = fetchResponse.errorCode(a_topic, a_partition);
log.info("[SimpleKafkaConsumer.run()] - Error fetching data from the Broker:" + leadBroker
+ " Reason: " + code);
if (numErrors > 5)
break;
if (code == ErrorMapping.OffsetOutOfRangeCode()) {
// We asked for an invalid offset. For simple case ask for
// the last element to reset
readOffset = getLastOffset(consumer, a_topic, a_partition, kafka.api.OffsetRequest.LatestTime(),
clientName);
continue;
}
consumer.close();
consumer = null;
leadBroker = findNewLeader(leadBroker, a_topic, a_partition, a_port);
continue;
}
numErrors = 0; long numRead = 0;
for (MessageAndOffset messageAndOffset : fetchResponse.messageSet(a_topic, a_partition)) {
long currentOffset = messageAndOffset.offset();
if (currentOffset < readOffset) {
log.info("[SimpleKafkaConsumer.run()] - Found an old offset: " + currentOffset + " Expecting: "
+ readOffset);
continue;
} readOffset = messageAndOffset.nextOffset();
ByteBuffer payload = messageAndOffset.message().payload(); byte[] bytes = new byte[payload.limit()];
payload.get(bytes);
System.out.println(String.valueOf(messageAndOffset.offset()) + ": " + new String(bytes, "UTF-8")); // Message deal enter
numRead++;
a_maxReads--;
} if (numRead == 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
}
}
}
if (consumer != null)
consumer.close();
} public static long getLastOffset(SimpleConsumer consumer, String topic, int partition, long whichTime,
String clientName) {
TopicAndPartition topicAndPartition = new TopicAndPartition(topic, partition);
Map<TopicAndPartition, PartitionOffsetRequestInfo> requestInfo = new HashMap<TopicAndPartition, PartitionOffsetRequestInfo>();
requestInfo.put(topicAndPartition, new PartitionOffsetRequestInfo(whichTime, 1));
kafka.javaapi.OffsetRequest request = new kafka.javaapi.OffsetRequest(requestInfo,
kafka.api.OffsetRequest.CurrentVersion(), clientName);
OffsetResponse response = consumer.getOffsetsBefore(request); if (response.hasError()) {
log.info("[SimpleKafkaConsumer.getLastOffset()] - Error fetching data Offset Data the Broker. Reason: "
+ response.errorCode(topic, partition));
return 0;
}
long[] offsets = response.offsets(topic, partition);
return offsets[0];
} /**
* @param a_oldLeader
* @param a_topic
* @param a_partition
* @param a_port
* @return String
* @throws Exception
* find next leader broker
*/
private String findNewLeader(String a_oldLeader, String a_topic, int a_partition, int a_port) throws Exception {
for (int i = 0; i < 3; i++) {
boolean goToSleep = false;
PartitionMetadata metadata = findLeader(m_replicaBrokers, a_port, a_topic, a_partition);
if (metadata == null) {
goToSleep = true;
} else if (metadata.leader() == null) {
goToSleep = true;
} else if (a_oldLeader.equalsIgnoreCase(metadata.leader().host()) && i == 0) {
// first time through if the leader hasn't changed give
// ZooKeeper a second to recover
// second time, assume the broker did recover before failover,
// or it was a non-Broker issue
//
goToSleep = true;
} else {
return metadata.leader().host();
}
if (goToSleep) {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
}
}
}
throw new Exception("Unable to find new leader after Broker failure. Exiting");
} private PartitionMetadata findLeader(List<String> a_seedBrokers, int a_port, String a_topic, int a_partition) {
PartitionMetadata returnMetaData = null;
loop: for (String seed : a_seedBrokers) {
SimpleConsumer consumer = null;
try {
consumer = new SimpleConsumer(seed, a_port, 100000, 64 * 1024, "leaderLookup");
List<String> topics = Collections.singletonList(a_topic);
TopicMetadataRequest req = new TopicMetadataRequest(topics);
kafka.javaapi.TopicMetadataResponse resp = consumer.send(req); List<TopicMetadata> metaData = resp.topicsMetadata();
for (TopicMetadata item : metaData) {
for (PartitionMetadata part : item.partitionsMetadata()) {
if (part.partitionId() == a_partition) {
returnMetaData = part;
break loop;
}
}
}
} catch (Exception e) {
log.error("Error communicating with Broker [" + seed + "] to find Leader for [" + a_topic + ", "
+ a_partition + "] Reason: " + e);
} finally {
if (consumer != null)
consumer.close();
}
}
if (returnMetaData != null) {
m_replicaBrokers.clear();
for (kafka.cluster.Broker replica : returnMetaData.replicas()) {
m_replicaBrokers.add(replica.host());
}
}
return returnMetaData;
}
}
4.总结
在使用 Kafka 低级消费 API 时,要明确我们所使用的业务场景,一般建议还是使用高级消费 API,除非遇到特殊需要。另外,在使用过程中,注意 Leader Broker 的处理,和 Offset 的管理。
5.结束语
这篇博客就和大家分享到这里,如果大家在研究学习的过程当中有什么问题,可以加群进行讨论或发送邮件给我,我会尽我所能为您解答,与君共勉!
Kafka - 消费接口分析的更多相关文章
- kafka 消费
前置资料 kafka kafka消费中的问题及解决方法: 情况1: 问题:脚本读取kafka 数据,写入到数据库,有时候出现MySQL server has gone away,导致脚本死掉.再次启 ...
- 分享一些 Kafka 消费数据的小经验
前言 之前写过一篇<从源码分析如何优雅的使用 Kafka 生产者> ,有生产者自然也就有消费者. 建议对 Kakfa 还比较陌生的朋友可以先看看. 就我的使用经验来说,大部分情况都是处于数 ...
- Go语言学习之12 etcd、contex、kafka消费实例、logagent
本节内容: 1. etcd介绍与使用 2. ElastcSearch介绍与使用 1. etcd介绍与使用 概念:高可用的分布式key-value存储,可以使用配置共享和服务发现 ...
- Kafka消费时报错:Producer connection to xxx:9092 unsuccessful
使用kafka消费数据时报Producer错误,具体错误如下: kafka.producer.SyncProducer:103 Producer connection to xxx:9092 unsu ...
- Kafka设计解析(十三)Kafka消费组(consumer group)
转载自 huxihx,原文链接 Kafka消费组(consumer group) 一直以来都想写一点关于kafka consumer的东西,特别是关于新版consumer的中文资料很少.最近Kafka ...
- 双十一问题:kafka消费能力低下原因思考
抛去cpu.内存等机器原因,在每个分区皆分配一个进程消费的情况下,利用扩机器来提高kafka消费速率已无能为力 此时发现,在实际洪峰时段的消费速率元达不到先前压测时的消费速率 原因思考: 1.洪峰时段 ...
- Kafka消费不到数据的特殊情况
我大约是把kafka消费不到数据的特殊情况都经历了一遍了吧= =. kafka消费不到数据的原因,首先检查配置之类的,如是否设置了group.id,对应的topic是否正确等等,这些不多说. 下面是我 ...
- Kafka消费分组和分区分配策略
Kafka消费分组,消息消费原理 同一个消费组里的消费者不能消费同一个分区,不同消费组的消费组可以消费同一个分区 Kafka分区分配策略 在 Kafka 内部存在两种默认的分区分配策略:Range 和 ...
- kafka消费失败
kafka消费失败 搞半天是路径错误,但是不会报错 改为 job 就对了
随机推荐
- 多重背包问题:悼念512汶川大地震遇难同胞——珍惜现在,感恩生活(HDU 2191)(二进制优化)
悼念512汶川大地震遇难同胞——珍惜现在,感恩生活 HDU 2191 一道裸的多重背包问题: #include<iostream> #include<algorithm> #i ...
- 关于.net 中Clipboard.GetDataObject() 之后读出数据读出的数据都是相同的解决方法
模拟键盘sendkey("^c") 多次复制之后 当使用Clipboard.GetDataObject() 读出数据都是一个值 经过多次尝试 提供一个解决方案 IDataObjec ...
- SQLSERVER监控复制并使用数据库邮件功能发告警邮件
SQLSERVER监控复制并使用数据库邮件功能发告警邮件 最近熬出病来了,都说IT行业伤不起,不说了,说回今天的正题 正题 上个月月底的时候因为要搬迁机房,需要将一个数据信息数据库先搬到我们的机房,然 ...
- 15 个很棒的 Bootstrap UI 界面编辑器
Bootstrap Magic BootSwatchr Bootstrap Live Editor Fancy Boot Style Bootstrap Lavish Bootstrap ThemeR ...
- 解决VS2013+IE11调试DevExpress ASP.NET MVC的性能问题
将一个MVC项目从12.2升级到14.2,VS2012升到2013,发现使用IE11调试非常慢卡死,CPU占用100%,后来经过排除,发现只有DevExpress的MVC项目有这个问题. 最后在Dev ...
- SQL Server 导入大数据脚本
.先使用win+r运行cmd .执行osql -S DESKTOP-RGBEDS3 -U sa -P liyang -d FGCPOE_十院 -i D:\数据库\script.sql 即可!
- [ALM]一步一步搭建MS ALM环境 - 安装域服务器
描述: 搭建并配置域服务器,先安装操作系统,配置网络,安装组件,配置域帐号 步骤: 1,打开Hyper-V Manager,参考[Hyper-V]使用操作系统模板创建新的虚拟机,先完成操作系统的安装, ...
- Android开发学习总结(六)—— APK反编译
学习和开发Android应用有一段时间了,今天写一篇博客总结一下Android的apk文件反编译.我们知道,Android应用开发完成之后,我们最终都会将应用打包成一个apk文件,然后让用户通过手机或 ...
- 地址转换协议ARP
地址转换协议ARP 在以太网协议中规定,同一局域网中的一台主机要和另一台主机进行直接通信,必须要知道目标主机的MAC地址.而在TCP/IP协议中,网络层和传输层只关心目标主机的IP地址.这就导致在以太 ...
- [JAVA] 一个可以编辑、编译、运行Java简单文件的记事本java实现
本来是Java课做一个仿windows记事本的实验,后来突然脑子一热,结果就给它加了一个编译运行Java文件的功能. 本工程总共大约3000行代码,基本上把所学的java界面.文件.控件的功能都包含在 ...