解惑 | 为什么我根据时间戳获得的offset为空呢?
放弃不难,但坚持很酷~
kafka_2.11-1.1.0
一、前言
最近有一个需求,要查询某一时间戳对应的offset值,于是就想到了使用 ./bin/kafka-run-class.sh kafka.tools.GetOffsetShell --time <timestamp> ,但是我在测试的时候,发现有的时间戳会获取不到offset,是空。但是明明指定的时间戳有上报数据,肯定有对应的 offset 的。于是就谷歌,找到了这篇帖子:
https://stackoverflow.com/questions/30030393/strange-behavior-of-kafka-tool-kafka-tools-getoffsetshell
其中已经有大佬给出了答案,但是我还是不求甚解,但起码知道了和 kafka log segments 有关系。
经过研究实践,明白了其中缘由,所以就有了这篇文章。
二、解惑
./bin/kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list message-1:9092 -topic test --time 后面的参数可以是 -1、-2、时间戳,其中 -1 会输出最新的 offset ;-2 会输出未过期最小的 offset ;时间戳这里具有迷惑性,它不能根据时间戳获取到精准匹配的 offset 。
Kafka 将数据存储在 “log segments” 里面,log segments 文件的大小受 log.segment.bytes 影响,默认为 1073741824 字节,也就是 1G 。当数据文件累积到 log.segment.bytes 的值以后,就会创建出新的日志文件,文件名称以分段时的那个 offset 命名,如下图所示:
每一个 xxx.log 文件都算作一个 segment,kafka.tools.GetOffsetShell --time 参数匹配的是 xxx.log 文件本身最后的修改时间,而不是偏移量本身的时间戳。
根据上面图片,举几个例子:
- 当 time 为 2020-09-16 11:59:20 时,获取的 offset 值为空。
- 当 time 大于等于 2020-09-16 12:00:20 并且 time 小于 2020-09-16 14:09:24 时,获取的 offset 值为 0,匹配的是 xxx.log 文件名称的那个 offset 。
- 当 time 大于等于 2020-09-16 14:09:24 时,获取的 offset 值为 1049942,匹配的是 xxx.log 文件名称的那个 offset 。
- 当 time 远大于 2020-09-16 14:09:24 时,获取的 offset 值为最新的 offset 值。
根据以上实践结果得知,一组时间戳均对应着同一个 offset 。所以这个命令 --time <timestamp> 只能匹配个大概的 offset 而已,无法精确。如果精确,可以调用 java api 来封装成接口或工具使用。
三、调用 kafka java api 获取时间戳对应的 offset,并封装成工具脚本
很纳闷,为什么官方不提供获取时间戳对应的精准的 offset 呢?既然官网没有,那我就用 java api 封装一个工具脚本吧。
先展示下效果:
./bin/getoffsetts --broker-list message-1:9092 --topic hiacloud-ts-v3 --time 1600222353353
脚本选项:
其实上面的 getoffsetts 脚本是执行的一个 java 类,java main() 方法。利用了 JCommander 。JCommander 是一个用于解析命令行参数的 Java 框架,利用 @Parameter 来接收命令参数。
在 main() 函数里面,创建 JCommander 对象,将 args 加载进去,
然后使用 consumer.offsetsForTimes(MaptimestampsToSearch) 来通过时间戳获取各分区对应的 offset 。
具体的代码已经上传到了
https://github.com/841809077/spring-boot-model/blob/master/src/main/java/com/example/main/OffsetTimestamp.java
可以直接粘贴到自己的项目中使用这个类查询 offset 。
这个项目算是一个快速可复用项目,支持自定义打包、统一异常处理等,感兴趣的可以下载下来看看。
- Universe入门
- 分享一款值得分享的写作工具
- 微信二次开发Java自定义菜单事件实现
- 微信OAuth授权获取用户OpenId-JAVA(个人经验)
- 【手写文字识别】-JavaAPI示例代码
- 【Python3-API】情感倾向分析示例代码
- SpringMVC+Hibernate +MySql+ EasyUI实现CRUD(一)
- 【Python3-API】通用文字识别示例代码
- Python入门教程之安装MyEclipse插件和安装Python环境
- AutoFlowLayout-多功能流式布局与网格布局控件
- RBAC新解:基于资源的权限管理(Resource-Based Access Control)
- 基于开源项目搭建属于自己的技术堆栈
- Redis整合Spring项目搭建实例
- SpringMVC+Hibernate +MySql+ EasyUI实现POI导出Excel(二)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 图书管理系统设计与实现—看这篇就够了
- 动态规划-区间DP
- 动态规划-树形DP
- 面试官:你精通多少种语言的 Hello World?
- Redis安装(Windows和Linux)详细图解
- 史上最详细版 头文件biso.h,graphics.h,libbgi.a
- ZooKeeper入门,这一篇给你讲的明明白白
- 数论-GCD、LCM、扩展欧几里得
- “豪 横”版 channel_v3.json,你确定不需要?
- Redis-性能测试(redis-benchmark)
- 一条贪吃蛇的使命——零基础入门贪吃蛇游戏
- 数论-快速幂、矩阵快速幂
- 字节一面,面试官告诉我链表掌握的不熟练
- 好玩、有趣的 Linux 命令学习神器 kmdr!
- 基于web的机票管理系统设计与实现(二)