发表于 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. hwclock详解

    linux下hwclock命令详解 hwclock(hardware clock) 功能说明:显示与设定硬件时钟. 语 法:hwclock [--adjust][--debug][--directis ...

  2. 人大金仓驱动包kingbasejdbc8.6.0.jar V8驱动jar包

    人大金仓驱动包kingbasejdbc8.6.0.jar V8驱动jar包 工作上要将kingbaseV8数据库整合到项目,我在官网找了半天,连个jdbc驱动包下载入口都找不到,简直就是官方文档毫无诚 ...

  3. Flutter GetX的事件监听

    Flutter GetX的事件监听 import 'package:flutter/material.dart'; import 'package:flutter_code/page/book/boo ...

  4. 最终,我决定将代码迁出x86架构!

    如今,我们几乎所有软件都建立在 x86 架构之上 ,在互联网漫长的演进过程中,各大公司拼尽全力在迭代上层架构.优化整体性能,开发者们该用的.能用的招儿想必都用上了,接下来呢?如果底层架构不出现大的革新 ...

  5. CodeLab:一款让你体验丝滑般的云化JupyterLab

    摘要:从AI开发特点着手,华为云AI DTSE技术布道师陈阳在DTT第五期带来主题为<云化JupyterLab:华为云CodeLab介绍>技术分享. DTSE Tech Talk是华为云开 ...

  6. 火山引擎DataLeap一站式数据治理解决方案及平台架构

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 在字节跳动内部,DataLeap数据平台数据治理团队致力于建立一站式.全链路的数据治理解决方案平台. 数据治理的概 ...

  7. 火山引擎数智平台ByteHouse入围稀土掘金《Top10 年度创新产品》

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 近日,国内开发者技术社区稀土掘金发布「2022 稀土掘金引力榜」,旨在盘点 2022 年在数字化转型领域内最具影响 ...

  8. 火山引擎 DataLeap:数据秒级生产,揭秘电商实时数仓最佳实践!

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 一年一度的「三八大促」刚刚落下帷幕,各大电商平台纷纷推出补贴.营销等玩法,力图推动持续增长.而电商平台持续增长,离 ...

  9. 简洁好用的python-pip包更新工具

    今天推荐一个github的开源工具 pkgu,支持以表格形式展示当前python环境下的有新版本的package的版本信息,并支持全部或部分更新这些已经过期或者有新版的库. 该工具目前还提供了 cac ...

  10. EXECL函数

    1 COUNTIF 对比两列数据,有相同的即计为1 找一列空白列,输入=COUNTIF(范围,条件),按回车,然后再点击表格右下角的"+" 就可以拉动持续执行这个函数 2 CONC ...