发表于 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. CSS3学习笔记-文字特效

    CSS3中提供了许多有趣和实用的文字特效,可以让我们的文本内容更加生动有趣,下面介绍一些常用的文字特效. 文本阴影 使用text-shadow属性可以为文本添加阴影效果,语法如下: text-shad ...

  2. 主控FC1179 U盘量产修复

    当我们的U盘出现如下情况的话,可以做为参考修复 第一步:可以用Chip Genius工具,查看U盘主控(可得知主控厂商:一芯 ,主控型号:FC1179). 第二步:下载主控相对应的量产工具(笔者已经上 ...

  3. 文心一言 VS 讯飞星火 VS chatgpt (38)-- 算法导论5.4 2题

    二.假设我们将球投入到b个箱子里,直到某个箱子中有两个球.每一次投掷都是独立的并且每个球落入任何箱子的机会均等.请问投球次数期望是多少? 文心一言: 这是一个典型的鸽巢原理(Pigeonhole Pr ...

  4. 文心一言 VS 讯飞星火 VS chatgpt (34)-- 算法导论5.3 1题

    一.Marceau 教授不同意引理 5.5 证明中使用的循环不变式.他对第1次送代之前循环不变式是否为真提出质疑.他的理由是,我们可以很容易宣称一个空数组不包含0排列.因此一个空的子数组包含一个0排列 ...

  5. flutter弹窗

    AlertDialog: 优点:Material风格的弹窗,具有灵活的布局和样式自定义能力.易于使用,并提供了标题.内容和操作按钮的选项. 缺点:对于复杂的自定义布局和样式可能不够灵活. Bottom ...

  6. 普通用户登录切换到root用户

    使用su命令: 在终端中输入以下命令并按Enter键: su - 输入root用户的密码,然后按Enter键. 如果密码正确,你将会切换为root用户,并且可以执行root用户的操作. 使用sudo命 ...

  7. Flink实时处理入门

    Flink实时处理入门 1.Flink框架介绍 Flink 诞生于欧洲的一个大数据研究项目 StratoSphere.它是由 3 所地处柏林的大学和欧洲其他一 些大学在 2010~2014 年共同进行 ...

  8. 【华为云技术分享】LwM2M协议的学习与分享

    [摘要] 本文主要对于LwM2M协议进行了简单的介绍,包括协议的体系架构以及特性.对象.资源.接口的定义等,希望对你有所帮助. 1协议简介 LwM2M(Lightweight Machine-To-M ...

  9. Spark的分布式存储系统BlockManager全解析

    摘要:BlockManager 是 spark 中至关重要的一个组件,在spark的运行过程中到处都有 BlockManager 的身影,只有搞清楚 BlockManager 的原理和机制,你才能更加 ...

  10. 如何快速上手 angular.js

    摘要:angular.js 准确的来说,应该不是一个框架,是一个 js 库,一个依赖于 jQuery 的进一步封装,去除繁琐的 DOM 操作,使用数据驱动的 MVC 模块化库. 哎,很难受,连续两个大 ...