consumer提交offset原理
1 数据结构
消费者的消费状态是保存在SubscriptionState类中的,而SubscriptionState有个重要的属性那就是assignment保存了消费者消费的partition及其partition的状态
public class SubscriptionState {
/* the pattern user has requested */
private Pattern subscribedPattern;
/* the list of topics the user has requested */
private final Set<String> subscription;
/* the list of topics the group has subscribed to (set only for the leader on join group completion) */
private final Set<String> groupSubscription;
/* the list of partitions the user has requested */
private final Set<TopicPartition> userAssignment;
/* the list of partitions currently assigned */
private final Map<TopicPartition, TopicPartitionState> assignment; // 关键, 保存了消费者消费的partition及其partition的状态
// ...
看下TopicPartitionState。TopicPartitionState用于表示消费者消费到该partition哪个位置了,需要注意的是position表示下一条需要消费的位置而不是已经消费的位置,拉取消息的时候就是根据position来确定需要拉取的第一条消息的offset
private static class TopicPartitionState {
private Long position; // 下一条消费哪个offset
private OffsetAndMetadata committed; // 已经提交的position
private boolean paused; // whether this partition has been paused by the user
private OffsetResetStrategy resetStrategy; // 重置position的时候的策略
// ...
}
public class OffsetAndMetadata implements Serializable {
private final long offset;
private final String metadata;
}
2 commit offset
以KafkaConsumer#commitSync为例来看下客户端是如何提交offset的
KafkaConsumer#commitSync
public void commitSync() {
acquire();
try {
commitSync(subscriptions.allConsumed()); // 调用SubscriptionState#allConsumed来获取已经消费的消息的位置,然后将其提交
} finally {
release();
}
}
public void commitSync(final Map<TopicPartition, OffsetAndMetadata> offsets) {
acquire();
try {
coordinator.commitOffsetsSync(offsets);
} finally {
release();
}
}
2.1 获取已经消费的位置
来看下SubscriptionState#allConsumed,从哪获取到消费到的位置。从下面的代码可以看出提交的offset就是TopicPartitionState#position
public Map<TopicPartition, OffsetAndMetadata> allConsumed() {
Map<TopicPartition, OffsetAndMetadata> allConsumed = new HashMap<>();
for (Map.Entry<TopicPartition, TopicPartitionState> entry : assignment.entrySet()) {
TopicPartitionState state = entry.getValue();
if (state.hasValidPosition())
allConsumed.put(entry.getKey(), new OffsetAndMetadata(state.position));// 关键,原来是将TopicPartitionState中的position封装成OffsetAndMetadata,即提交的是TopicPartitionState#position
}
return allConsumed;
}
2.2 发送到网络
获取到消费到的offset位置后,最终是通过ConsumerCoordinator#sendOffsetCommitRequest将offset发送到coordinator的
private RequestFuture<Void> sendOffsetCommitRequest(final Map<TopicPartition, OffsetAndMetadata> offsets) {
if (coordinatorUnknown()) // 必须获取coordinator
return RequestFuture.coordinatorNotAvailable();
if (offsets.isEmpty())
return RequestFuture.voidSuccess();
// create the offset commit request
Map<TopicPartition, OffsetCommitRequest.PartitionData> offsetData = new HashMap<>(offsets.size());
for (Map.Entry<TopicPartition, OffsetAndMetadata> entry : offsets.entrySet()) {
OffsetAndMetadata offsetAndMetadata = entry.getValue();
offsetData.put(entry.getKey(), new OffsetCommitRequest.PartitionData(
offsetAndMetadata.offset(), offsetAndMetadata.metadata())); // 以TopicPartition为key, offsetAndMetadat组成request中的数据
}
OffsetCommitRequest req = new OffsetCommitRequest(this.groupId,
this.generation,
this.memberId,
OffsetCommitRequest.DEFAULT_RETENTION_TIME,
offsetData);
log.trace("Sending offset-commit request with {} to coordinator {} for group {}", offsets, coordinator, groupId);
return client.send(coordinator, ApiKeys.OFFSET_COMMIT, req)
.compose(new OffsetCommitResponseHandler(offsets));// 发送到coordinator
}
2.3 处理response
从上面代码最后一行可以看出处理response的逻辑在OffsetCommitResponseHandler中。如果提交成功,那么会将TopicPartitionState#position更新到TopicPartitionState#commit
private class OffsetCommitResponseHandler extends CoordinatorResponseHandler<OffsetCommitResponse, Void> {
private final Map<TopicPartition, OffsetAndMetadata> offsets;
public OffsetCommitResponseHandler(Map<TopicPartition, OffsetAndMetadata> offsets) {
this.offsets = offsets;
}
@Override
public OffsetCommitResponse parse(ClientResponse response) {
return new OffsetCommitResponse(response.responseBody());
}
@Override
public void handle(OffsetCommitResponse commitResponse, RequestFuture<Void> future) {
sensors.commitLatency.record(response.requestLatencyMs());
Set<String> unauthorizedTopics = new HashSet<>();
for (Map.Entry<TopicPartition, Short> entry : commitResponse.responseData().entrySet()) {
TopicPartition tp = entry.getKey();
OffsetAndMetadata offsetAndMetadata = this.offsets.get(tp); // this.offsets即sendOffsetCommitRequest中的入参,这点很关键
long offset = offsetAndMetadata.offset();
Errors error = Errors.forCode(entry.getValue());
if (error == Errors.NONE) {
if (subscriptions.isAssigned(tp))
subscriptions.committed(tp, offsetAndMetadata); // 更新TopicPartitionState#committed为发送的时候的TopicPartitionState#position
}
// ...
}
}
}
3 总结
- 下一条要消费的消息的offset就是TopicPartitionState#position
- 提交offset的时候即将TopicPartitionState#position发送到coordinator
- 提交成功后则将TopicPartitionState#committed更新为TopicPartitionState#position
consumer提交offset原理的更多相关文章
- Kafka提交offset机制
在kafka的消费者中,有一个非常关键的机制,那就是offset机制.它使得Kafka在消费的过程中即使挂了或者引发再均衡问题重新分配Partation,当下次重新恢复消费时仍然可以知道从哪里开始消费 ...
- 关于SpringKafka消费者的几个监听器:[一次处理单条消息和一次处理一批消息]以及[自动提交offset和手动提交offset]
自己在使用Spring Kafka 的消费者消费消息的时候的实践总结: 接口 KafkaDataListener 是spring-kafka提供的一个供消费者接受消息的顶层接口,也是一个空接口; pu ...
- spring-kafka手动提交offset
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
- kafka消费端提交offset的方式
Kafka 提供了 3 种提交 offset 的方式 自动提交 复制 1234 consumer.commitSync(); 手动异步提交 offset 复制 1 consumer.commitAsy ...
- Spring-Kafka —— 消费后不提交offset情况的分析总结
最近在使用kafka,过程中遇到了一些疑问,在查阅了一些资料和相关blog之后,关于手动提交offset的问题,做一下总结和记录. 消费端手动提交offset代码如下: /** * 这是手动提交的消费 ...
- kafka consumer 自动提交 offset
org.apache.kafka.clients.consumer.KafkaConsumer#pollOnce private Map<TopicPartition, List<Cons ...
- Kafka配置项unclean.leader.election.enable造成consumer出现offset重置现象
消费端出现offset重置为latest, earliest现象,类似log: (org.apache.kafka.clients.consumer.internals.Fetcher.handleF ...
- kafka consumer 指定 offset,进行消息回溯
kafka consumer 如何根据 offset,进行消息回溯?下面的文档给出了 demo: https://cwiki.apache.org/confluence/display/KAFKA/0 ...
- spark提交运算原理
前面几天元旦过high了,博客也停了一两天,哈哈,今天我们重新开始,今天我们介绍的是spark的原理 首先先说一个小贴士: spark中,对于var count = 0,如果想使count自增,我们不 ...
随机推荐
- ASP.NET Core 6框架揭秘实例演示[10]:Options基本编程模式
依赖注入使我们可以将依赖的功能定义成服务,最终以一种松耦合的形式注入消费该功能的组件或者服务中.除了可以采用依赖注入的形式消费承载某种功能的服务,还可以采用相同的方式消费承载配置数据的Options对 ...
- Hadoop - MapReduce学习笔记(详细)
第1章 MapReduce概述 定义:是一个分布式运算程序的编程框架 优缺点:易于编程.良好的扩展性.高容错性.适合PB级以上数据的离线处理 核心思想:MapReduce 编程模型只能包含一个Map ...
- Codeforces Round #769 (Div. 2)D,E
D.New Year Concert 传送门 题目大意: 一个长为 N ( 1 ≤ N ≤ 2 × 1 0 5 ) N(1\leq N\leq2\times 10^5) N(1≤N≤2×105)的序列 ...
- C语言中一些知识点
1.学编程:找代码,读代码,改代码,写代码:少看书.但少看不是不看,看一本书要限制在一天内.任务是通读书中主要的概念,明白大致是干什么用的,形成一个大概的框架:然后一周内干掉书中全部代码,代码不理解再 ...
- Python面向对象之数据封装的应用及配置文件
面向对象封装的应用 1.配置文件 1.1 ini配置文件 ini 文件是Initialzation File的缩写,平时用于存储软件的配置文件.例如:MySQL数据库的配置文件(my.ini) [my ...
- 微信小程序如何测试?
不需要安装,只要在微信里找到这个小程序打开即可使用,由于小程序的便捷,如今越来越多的平台开发方都纷纷推出自身的小程序应用. 那我们该如何进行微信小程序测试呢? 1.功能测试 功能测试以需求文档和交互视 ...
- MAC VMware配置Kali linux
一.部署kali虚拟机 1.选择vmaware fusion12,放弃parallels,因为我用的是MAC系统,所以提供的是VMware的mac版本 2.VMware fusion12邀请码: ZF ...
- .NET的两种部署模式,了解一下
前言 以往部署程序一直是习惯性先安装运行时环境,然后再将发布打包好的程序运行起来:但当多个程序依赖不同版本框架平台时,如果部署在同一台机器上,那就需要在同一台机器上安装多个版本的运行时,总感觉有点不太 ...
- 矩池云上如何快速安装tensorRT
国内镜像 https://mirrors.cloud.tencent.com/nvidia-machine-learning/ubuntu1804/x86_64/ 检查系统版本 source /etc ...
- SpringBoot连接Redis (Sentinel模式&Cluster模式)
一.引入pom <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...