发表于 2020-11-29  分类于 Java , Apache , JavaClass , Kafka  Valine: 0

Kafka Consumer API

Kafka 提供了两套 API 给 Consumer

  • The high-level Consumer API
  • The SimpleConsumer API

第一种高度抽象的 Consumer API,它使用起来简单、方便,但是对于某些特殊的需求我们可能要用到第二种更底层的 API。

SimpleConsumer 优势

那么第二种 The SimpleConsumer API 能够帮助我们做哪些事情?

  • 一个消息读取多次
  • 在一个处理过程中只消费 Partition 其中的一部分消息
  • 添加事务管理机制以保证消息被处理且仅被处理一次

SimpleConsumer 弊端

使用 SimpleConsumer 有哪些弊端呢?

  • 必须在程序中跟踪 offset 值
  • 必须找出指定 Topic Partition 中的 lead broker
  • 必须处理 broker 的变动

SimpleConsumer 步骤

使用 SimpleConsumer 的步骤

  1. 从所有活跃的 broker 中找出哪个是指定 Topic Partition 中的 leader broker
  2. 找出指定 Topic Partition 中的所有备份 broker
  3. 构造请求
  4. 发送请求查询数据
  5. 处理 leader broker 变更

命令行获取 topic 信息总量

$ bin/kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list XXX1:9092 --topic topicName1 --time -1
topicName1:2:73454
topicName1:5:73006
topicName1:4:73511
topicName1:1:73493
topicName1:3:73019
topicName1:0:72983 $ bin/kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list XXX1:9092 --topic topicName1 --time -2
topicName1:2:0
topicName1:5:0
topicName1:4:0
topicName1:1:0
topicName1:3:0
topicName1:0:0
 

--time -1 表示要获取指定 topic 所有分区当前的最大位移,**--time -2** 表示获取当前最早位移。

两个命令的输出结果相减便可得到所有分区当前的消息总数。

分区当前的消息总数 = [--time-1] - [--time-2]

相减是因为随着 kafka 的运行,topic 中有的消息可能会被删除,因此 --time -1 的结果其实表示的是历史上该 topic 生产的最大消息数,如果用户要统计当前的消息总数就必须减去 --time -2 的结果。

本例中没有任何消息被删除,故 --time -2 的结果全是 0,表示最早位移都是 0,消息总数等于历史上发送的消息总数。

Java 获取 topic 消息总量

high-level Consumer

The high-level Consumer API 获取 Kafka 指定 topic 的消息总量:

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.stream.Collectors; import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.TopicPartition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class KafkaOffsetTools {
private final static Logger logger = LoggerFactory.getLogger(KafkaOffsetTools.class); public static final String KAFKA_BOOTSTRAP_SERVERS = "XXX1:9092,XXX2:9092,XXX3:9092";
public static final List<String> TOPIC_LIST = Arrays.asList("topicName1","topicName2"); public static void main(String[] args) {
for(String topic: TOPIC_LIST) {
long totolNum = totalMessageCount(topic, KAFKA_BOOTSTRAP_SERVERS);
System.out.println(topic+":"+totolNum);
}
} public static long totalMessageCount(String topic, String brokerList) {
Properties props = new Properties();
props.put("bootstrap.servers", brokerList);
props.put("group.id", "test-group");
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"); try (KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props)) {
List<TopicPartition> tps = Optional.ofNullable(consumer.partitionsFor(topic))
.orElse(Collections.emptyList())
.stream()
.map(info -> new TopicPartition(info.topic(), info.partition()))
.collect(Collectors.toList());
Map<TopicPartition, Long> beginOffsets = consumer.beginningOffsets(tps);
Map<TopicPartition, Long> endOffsets = consumer.endOffsets(tps); return tps.stream().mapToLong(tp -> endOffsets.get(tp) - beginOffsets.get(tp)).sum();
}
}
}
 

输出结果:

topicName1:5301171
topicName2:439466
 

SimpleConsumer

The SimpleConsumer API 获取 Kafka 指定 topic 的消息总量:

import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import kafka.api.PartitionOffsetRequestInfo;
import kafka.common.TopicAndPartition;
import kafka.javaapi.OffsetRequest;
import kafka.javaapi.OffsetResponse;
import kafka.javaapi.PartitionMetadata;
import kafka.javaapi.TopicMetadata;
import kafka.javaapi.TopicMetadataRequest;
import kafka.javaapi.TopicMetadataResponse;
import kafka.javaapi.consumer.SimpleConsumer; public class KafkaOffsetTools { private final static Logger logger = LoggerFactory.getLogger(KafkaOffsetTools.class); public static final String KAFKA_BOOTSTRAP_SERVERS = "XXX1:9092,XXX2:9092,XXX3:9092";
public static final List<String> TOPIC_LIST = Arrays.asList("topicName1","topicName2"); public static void main(String[] args) {
String[] kafkaHosts = KAFKA_BOOTSTRAP_SERVERS.split(",");
List<String> seeds = Arrays.asList(kafkaHosts);
KafkaOffsetTools kot = new KafkaOffsetTools();
Map<String, Integer> topicNumMap = new HashMap<String, Integer>();
for (String topicName : TOPIC_LIST) {
TreeMap<Integer, PartitionMetadata> metadatas = kot.findLeader(seeds, topicName);
int logSize = 0;
for (Entry<Integer, PartitionMetadata> entry : metadatas.entrySet()) {
int partition = entry.getKey();
String leadBroker = entry.getValue().leader().host();
String clientName = "Client_" + topicName + "_" + partition;
SimpleConsumer consumer = new SimpleConsumer(leadBroker, entry.getValue().leader().port(), 100000, 64 * 1024, clientName);
long readOffset = getLastOffset(consumer, topicName, partition, kafka.api.OffsetRequest.LatestTime(), clientName);
logSize += readOffset;
if (consumer != null) {
consumer.close();
}
}
topicNumMap.put(topicName, logSize);
}
System.out.println(topicNumMap.toString());
} private TreeMap<Integer, PartitionMetadata> findLeader(List<String> a_seedBrokers, String a_topic) {
TreeMap<Integer, PartitionMetadata> map = new TreeMap<Integer, PartitionMetadata>();
for (String seed : a_seedBrokers) {
SimpleConsumer consumer = null;
try {
String[] hostAndPort = seed.split(":");
consumer = new SimpleConsumer(hostAndPort[0], Integer.valueOf(hostAndPort[1]), 100000, 64 * 1024, "leaderLookup" + new Date().getTime());
List<String> topics = Collections.singletonList(a_topic);
TopicMetadataRequest req = new TopicMetadataRequest(topics);
TopicMetadataResponse resp = consumer.send(req); List<TopicMetadata> metaData = resp.topicsMetadata();
for (TopicMetadata item : metaData) {
for (PartitionMetadata part : item.partitionsMetadata()) {
map.put(part.partitionId(), part);
}
}
} catch (Throwable e) {
logger.error("Broker [" + seed + "] to find Leader for [" + a_topic + "] Reason: " + e.getMessage(), e);
} finally {
if (consumer != null) {
consumer.close();
}
}
}
return map;
} 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));
OffsetRequest request = new kafka.javaapi.OffsetRequest(requestInfo, kafka.api.OffsetRequest.CurrentVersion(), clientName);
OffsetResponse response = consumer.getOffsetsBefore(request); if (response.hasError()) {
logger.error("Error fetching data Offset Data the Broker. Reason: " + response.errorCode(topic, partition));
return 0;
}
long[] offsets = response.offsets(topic, partition);
return offsets[0];
}
}
 

输出结果:

{topicName1=5301171, topicName2=439466}
 
相关文章

[转帖]Java 获取 Kafka 指定 topic 的消息总量的更多相关文章

  1. 关于怎么获取kafka指定位置offset消息(转)

    1.在kafka中如果不设置消费的信息的话,一个消息只能被一个group.id消费一次,而新加如的group.id则会被“消费管理”记录,并指定从当前记录的消息位置开始向后消费.如果有段时间消费者关闭 ...

  2. java获取系统指定时间年月日

    java获取系统指定时间年月日 private String setDateTime(String falg) { Calendar c = Calendar.getInstance(); c.set ...

  3. Kafka Java API获取非compacted topic总消息数

    目前Kafka并没有提供直接的工具来帮助我们获取某个topic的当前总消息数,需要我们自行写程序来实现.下列代码可以实现这一功能,特此记录一下: /** * 获取某个topic的当前消息数 * Jav ...

  4. java api如何获取kafka所有Topic列表,并放置为一个list

    kafka内部所有的实现都是通过TopicCommand的main方法,通过java代码调用API,TopicCommand.main(options)的方式只能打印到控制台,不能转换到一个list. ...

  5. Java 获取字符串指定下标位置的值 charAt()

    Java手册 charAt public char charAt(int index) 返回指定索引处的 char 值.索引范围为从 0 到 length() - 1.序列的第一个 char 值位于索 ...

  6. JAVA获取当前日期指定月份后(多少个月后)的日期

    环境要求:使用jdk1.8 package com.date; import java.text.ParseException; import java.text.SimpleDateFormat; ...

  7. java 获取网页指定内容-2(实践+修改)

    import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; ...

  8. java 获取网页指定内容

    import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; ...

  9. JAVA获取当前日期指定天数之后的日期

    /** * 获取day天之后的日期 * @param day 天数 * @return */ public static String getDate(int day){ Calendar calen ...

  10. 工具篇-Spark-Streaming获取kafka数据的两种方式(转载)

    转载自:https://blog.csdn.net/weixin_41615494/article/details/7952173 一.基于Receiver的方式 原理 Receiver从Kafka中 ...

随机推荐

  1. pacman下载时经常出现Operation too slow. Less than 1 bytes/sec transferred the last 10 seconds

    问题 我在manjaro系统上使用pacman下载软件的时候,经常出现以下报错: 错误:无法从 mirrors.tuna.tsinghua.edu.cn : Operation too slow. L ...

  2. Linux 逻辑卷管理

    如果用标准分区在硬盘上创建了文件系统,为已有的文件系统添加额外的空间是一件十分痛苦的事情.只能在已有的硬盘上的可用空间范围内调整分区大小,如果硬盘空间不够的话,就只能换一个大容量的硬盘,然后手动将已有 ...

  3. 2023-08-28:用go语言编写。给你一个正整数数组nums, 同时给你一个长度为 m 的整数数组 queries。 第 i 个查询中,你需要将 nums 中所有元素变成 queries[i] 。

    2023-08-28:用go语言编写.给你一个正整数数组nums, 同时给你一个长度为 m 的整数数组 queries. 第 i 个查询中,你需要将 nums 中所有元素变成 queries[i] . ...

  4. Java 查找并替换PDF中的指定文本

    本文介绍通过Java程序批量替换PDF中的指定文本内容. 1. 程序环境准备如下: 程序使用环境如图,需要注意的是,本文使用了免费版的PDF jar工具:另外JDK版本建议使用高版本更佳.   jar ...

  5. 火山引擎 DataTester:“在字节,A/B 实验是一种信仰”

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,并进入官方交流群 进入数字经济时代,要用数据驱动业务增长已经成为各个行业的共识,但很多企业还没能真正掌握这项能力.如何最大限度转化数据价值, ...

  6. Solon 生态插件清单

    Solon 是一个微型的Java开发框架.强调,克制 + 简洁 + 开放的原则:力求,更小.更快.更自由的体验.支持:RPC.REST API.MVC.Micro service.WebSocket. ...

  7. why哥这里有一道Dubbo高频面试题,请查收。

    这是why的第 64 篇原创文章 荒腔走板 大家好,我是 why,欢迎来到我连续周更优质原创文章的第 64 篇.老规矩,先荒腔走板聊聊其他的. 上面这图是我之前拼的一个拼图. 我经常玩拼图,我大概拼了 ...

  8. PPT 求职应聘:如何利用时间轴去展示

    顺序逻辑的梳理 线条 竖直排列 图形结合

  9. 【Go】go语言变量类型 常量 函数基础 函数高级 setuptools将python项目打包 前后端联调

    昨日回顾 使用setuptools将python项目打包 # 详细: python---->setuptools-->whl包结构 https://zhuanlan.zhihu.com/p ...

  10. Python pickle 二进制序列化和反序列化 - 数据持久化

    模块 pickle 实现了对一个 Python 对象结构的二进制序列化和反序列化. "pickling" 是将 Python 对象及其所拥有的层次结构转化为一个字节流的过程,而 & ...