Kafka入门实战教程(9):深入了解Offset
1 什么是offset?
Offset,消息位移,它表示分区中每条消息的位置信息,是一个单调递增且不变的值。换句话说,offset可以用来唯一的标识分区中每一条记录。
消费者消费完一条消息记录之后,需要提交offset来告诉Kafka Broker自己消费到哪里了。
2 Offset存在哪里?
Kafka 0.9.0版本以前,这些数值维护在zookeeper中,但是zookeeper并不适合大量写入(涉及网络通讯),因此后来做了改动。
Kafka 0.9.0版本以后,这些数据维护在kafka的_consumer_offsets这个topic下。
_consumer_offsets这个topic中采用了key/value的方式存储数据,key为group.id+topic+分区号,而value则是当前offset的值。每隔一段时间,Kafka内部会对这个topic进行compact,也就是每个group.id+topic+分区号就保留最新数据。
3 提交offset的方式
自动提交offset
Kafka为了使我们能够专注于自己的业务逻辑,提供了自动提交offset的功能,这也是默认配置项。
我们需要关注以下两个配置参数:
enable.auto.commit:是否开启自动提交offset功能,默认是true
auto.commit.interval.ms:自动提交offset的时间间隔,默认是5s
在Confluent.Kafka中如下配置即可:
var config = new ConsumerConfig
{
......
EnableAutoCommit = true, // 开启AutoCommit,默认为true,因此可以不显示配置
AutoCommitIntervalMs = 3000, // 自动提交offset的时间间隔,默认为5s
};
手动提交offset
虽然自动提交offset带来了很大的便利,但是在消息的可靠性上不太容易掌控,因此Kafka也提供了手动提交offset这个功能。
在Confluent.Kafka中可以这样设置:
var config = new ConsumerConfig
{
...
// Disable auto-committing of offsets.
EnableAutoCommit = false
} ... while (!cancelled)
{
var consumeResult = consumer.Consume(cancellationToken); // process message here. if (consumeResult.Offset % commitPeriod == 0)
{
try
{
consumer.Commit(consumeResult);
}
catch (KafkaException e)
{
Console.WriteLine($"Commit error: {e.Error.Reason}");
}
}
}
但是需要注意的是,使用Commit方法提交位移会产生阻塞,影响吞吐量。
在Confluent.Kafka中还提供了一种不产生阻塞的方式:Store Offsets。它的原理是允许Kafka在后台线程帮我们自动提交,但是offset的偏移量更新由我们手动来控制,兼顾了性能与可靠性,示例代码如下:
var config = new ConsumerConfig
{
...
EnableAutoCommit = true // (the default)
EnableAutoOffsetStore = false
} ... while (!cancelled)
{
var consumeResult = consumer.Consume(cancellationToken); // process message here. consumer.StoreOffset(consumeResult);
}
4 指定offset消费方式
三种主要方式
Kafka针对offset的消费方式提供了三种类型:earliest | latest | none,默认是latest,即从最新的offset开始消费。
(1)earliest:自动将偏移量 重置为最早的,--fromfromfrom。
(2)latest(默认值):自动将偏移量重置为最新偏移量。
(3)none :如果未找到消费者组的先前偏移量,则向抛出异常。
在Confluent.Kafka中,Consumer可以进行如下配置:
var config = new ConsumerConfig
{
...
AutoOffsetReset = AutoOffsetReset.Earliest // 从最早的开始消费起
AutoOffsetReset = AutoOffsetReset.Latest // 从最新的开始消费起
AutoOffsetReset = AutoOffsetReset.Error // 如果未找到消费组的先前偏移量,则抛出错误异常
}
指定时间消费
在实际场景下,可能会遇到最近消费的几个小时数据异常,需要重新按照某个时间进行消费,比如:要求按照时间消费前一天的消息记录。
因此,我们可以通过下面的工具脚本将消费者组的位移进行重置:
bin/kafka-consumer-groups.sh --bootstrap-server kafka1:9092,kafka2:9092,kakfa3:9092 --group test-group --reset-offsets --to-datetime 2022-07-07T20:00:00.000 --execute
由于Confluent.Kafka组件并未提供这个功能,所以建议使用工具脚本进行按日期重设offset。
5 漏消费与重复消费
漏消费
在Consumer的消费逻辑中,如果先提交了offset后消费,有可能出现数据的漏消费。
例如,在某个场景中,我们设置了offset为手动提交,当offset被提交时,数据还在内存中未落盘,此时刚好消费者线程被kill掉了,那么offset已经提交,但是数据尚未进行真正的处理,导致这部分内存中的数据丢失。
解决办法就是:先消费后再提交offset。
重复消费
如果开启了自动提交offset,在某些场景下,如果在提交后的某个时间(该时间尚未达到自动提交的时间间隔如5s)时Consumer挂了,可能会导致Consumer重启后从上一次成功提交的offset处继续消费,从而出现重复消费。
解决办法就是:关闭自动提交,手动提交offset。但仍然存在会重复消费的可能性,因此消费者端还是需要进行保证幂等性的处理。
例如,我们可以通过使用具有事务数据存储的IMessageTracker来跟踪消息ID,那么消费端的代码可能长下面这样子(该示例基于CAP组件做示例代码):
readonly IMessageTracker _messageTracker; public SomeMessageHandler(IMessageTracker messageTracker)
{
_messageTracker = messageTracker;
} [CapSubscribe]
public async Task Handle(SomeMessage message)
{
if (await _messageTracker.HasProcessed(message.Id))
{
return;
} // do the work here
// ... // remember that this message has been processed
await _messageTracker.MarkAsProcessed(messageId);
}
至于 IMessageTracker 的实现,可以使用诸如Redis或者数据库等存储消息Id和对应的处理状态。
6 消费数据积压
消息数据积压恐怕是比较常见的线上问题了,一般来说,Kafka数据积压问题需要分成两个维度来分析。
Kafka消费能力不足
如果是Kafka消费能力不足,可以考虑给Kafka增加Topic的分区数,并同步增加消费者Consumer的实例数,谨记:分区数=消费者数(二者缺一不可)。
bin/kafka-topics.sh --bootstrap-server kafka1:9092,kafka2:9092,kafka3:9092 --alter --topic test --partitions 5
注意:分区数可以增加,但是不能减少!
Consumer数据处理不及时
如果是Consumer的数据处理不够及时,那么可以考虑提高每批次拉取的数量。如果批次拉取数据过少(拉取数据时间/处理时间 < 生产速度),当处理的数据小于生产的数据时,也会产生数据积压。
对应的Consumer端参数解释如下:
需要注意的是,如果单纯只扩大一次poll拉取数据的最大条数,可能它会收到消息最大字节数的限制,因此最好是同时更新两个参数的值。
7 总结
本文总结了offset的基础概念、存储位置、消费方式,扩展了两个常见问题:漏消费与重复消费,引出了一个消费数据积压问题,希望能对你有所帮助!
参考资料
极客时间,胡夕《Kafka核心技术与实战》
B站,尚硅谷《Kafka 3.x入门到精通教程》
Kafka入门实战教程(9):深入了解Offset的更多相关文章
- Kafka入门实战教程(7):Kafka Streams
1 关于流处理 流处理平台(Streaming Systems)是处理无限数据集(Unbounded Dataset)的数据处理引擎,而流处理是与批处理(Batch Processing)相对应的.所 ...
- 转 Kafka入门经典教程
Kafka入门经典教程 http://www.aboutyun.com/thread-12882-1-1.html 问题导读 1.Kafka独特设计在什么地方?2.Kafka如何搭建及创建topic. ...
- Kafka入门经典教程
本帖最后由 desehawk 于 2015-5-3 00:45 编辑问题导读 1.Kafka独特设计在什么地方?2.Kafka如何搭建及创建topic.发送消息.消费消息?3.如何书写Kafka程 ...
- Kafka入门经典教程【转】
问题导读 1.Kafka独特设计在什么地方?2.Kafka如何搭建及创建topic.发送消息.消费消息?3.如何书写Kafka程序?4.数据传输的事务定义有哪三种?5.Kafka判断一个节点是否活着有 ...
- [入门帮助] Kafka入门经典教程
问题导读 1.Kafka独特设计在什么地方?2.Kafka如何搭建及创建topic.发送消息.消费消息?3.如何书写Kafka程序?4.数据传输的事务定义有哪三种?5.Kafka判断一个节点是否活着有 ...
- ZooKeeper入门实战教程(一)-介绍与核心概念
1.ZooKeeper介绍与核心概念1.1 简介ZooKeeper最为主要的使用场景,是作为分布式系统的分布式协同服务.在学习zookeeper之前,先要对分布式系统的概念有所了解,否则你将完全不知道 ...
- 《OD大数据实战》Kafka入门实例
官网: 参考文档: Kafka入门经典教程 Kafka工作原理详解 一.安装zookeeper 1. 下载zookeeper-3.4.5-cdh5.3.6.tar.gz 下载地址为: http://a ...
- Kafka入门教程(二)
转自:https://blog.csdn.net/yuan_xw/article/details/79188061 Kafka集群环境安装 相关下载 JDK要求1.8版本以上. JDK安装教程:htt ...
- mybatis实战教程(mybatis in action),mybatis入门到精通
转自:http://www.yihaomen.com/article/java/302.htm (读者注:其实这个应该叫做很基础的入门一下下,如果你看过hibernate了那这个就非常的简单) (再加 ...
- mybatis实战教程(mybatis in action),mybatis入门到精通(转)
转自:http://www.yihaomen.com/article/java/302.htm (读者注:其实这个应该叫做很基础的入门一下下,如果你看过Hibernate了那这个就非常的简单) (再加 ...
随机推荐
- 深入理解Hadoop读书笔记-1
背景 公司的物流业务系统目前实现了使用storm集群进行过门事件的实时计算处理,但是还有一个需求,我们需要存储每个标签上传的每条明细数据,然后进行定期的标签报表统计,这个是目前的实时计算框架无法满足的 ...
- 【C语言】Linux 飞翔的小鸟
[C语言]Linux 飞翔的小鸟 零.环境部署 安装Ncurses库 sudo apt-get install libncurses5-dev 壹.编写代码 代码如下: bird.c #include ...
- IntelliJ IDEA 社区版没有 Spring Initializr
RT 解决办法 打开文件 - 设置 - 插件 输入 Spring 找到插件 Spring Assistant 并安装 下载可能会需要一点点时间. 重启 IDEA 后,新建项目就可以看见 Spring ...
- ASP.NET 简单实现数字时钟
每隔1000毫秒(1s)获取一次当前时间 <asp:ScriptManager ID="ScriptManager_Time" runat="server" ...
- SQL语句(一)—— DDL
SQL 全称 Structured Query Language,结构化查询语言.操作关系型数据库的编程语言,定义了一套操作关系型数据库统一标准 . 一.SQL 基础知识 (一)SQL 通用语法 在学 ...
- python API 之 fastapi
为什么选择 FastAPI? 高性能:基于 Starlette 和 Uvicorn,支持异步请求处理 开发效率:自动交互文档.类型提示.代码自动补全 现代标准:兼容 OpenAPI 和 JSON Sc ...
- @PathVaribale
/** * @pathVaribale * 作用: 用于获取url 中的占位符的值. * 例如:请求 url 中 /delete/{id},这个{id}就是 url 占位符. * url 支持占位符是 ...
- 格林威治时间(Tue Jan 01 00:00:00 CST 2019)转Date
Excel导入时后台接受日期格式数据为[格林威治时间](例:Tue Jan 01 00:00:00 CST 2019) 格林威治时间转Date package com.cn; import java. ...
- MySQL插入异常:SQL state [HY000]; error code [1366]-----(utf8mb4)
发现爬虫软件,爬取数据不及时,查询服务器日志发现异常: SQL state [HY000]; error code [1366] java.sql.SQLException: Incorrect st ...
- 康谋分享 | 从CAN到CAN FD:ADTF在汽车网络中的应用
随着汽车电子技术的发展,车辆上配备了越来越多的电子装置,这些设备多采用点对点的方式通信,这也导致了车内存在庞大的线束.造成汽车制造和安装的困难并进一步降低汽车的配置空间,汽车总线逐步开始向网络化方向发 ...