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自增,我们不 ...
随机推荐
- DT时代,优秀的BI工具应该具备哪些功能
马云曾在一次演讲中说:"人类正从IT时代走向DT时代."那DT究竟是什么,和IT有什么不同呢?我们对IT非常熟悉,它是信息技术(InformationTechnology)的英文缩 ...
- 号外!号外!这个敏捷高效的大数据bi看板可以免费使用啦!
随着信息革命的深入推进,数据已经成为国家基础性战略资源,各个行业开始重视数据分析,企业不同,数据分析需求当然不一样,如销售行业需要对商品进行销售分析:网站运营需要进行用户.渠道.流量等信息分析:制造行 ...
- 为什么用Python,高级的Python是一种高级编程语言
Python特性 如果有人问我Python最大的特点是什么,我会毫不犹豫地告诉他:它简单易学,功能强大.作为一个纯自由软件,Python有许多优点: 很简单.基于"优雅".&quo ...
- IComparer、IComparable、StringComparison枚举、CultureInfo 的用法
IEnumerable<T> 和 IEnumerator<T>.泛型版本是新式代码的首要选项. InvariantCulture:程序间.程序数据库.程序网络交互用Invari ...
- 为Visual Studio 2019设置 更改皮肤
下载主题插件:Color Themes for Visual Studio 安装插件 下载完成后 关闭vs2019 完成初始化,初始化完成后,再次打开软件进行配置. 卸载插件 点击卸载,然后关闭vs2 ...
- c++ cgi执行shell命令l发送qq邮件mailx
#include <cstdlib> //随机数 #include <iostream> #include <cstdio> //popen函数调用的需要 #inc ...
- tomcat启动错误:Error running 'Tomcat 9.0.34': Address localhost:1099 is already in use
解决方法博客地址:https://blog.csdn.net/weixin_46697202/article/details/105782670
- Qt:QList、QStringList
QList 0.说明 QList<T> 一个QList是存储相同类型一组数据的列表. QStringList是从QList<String>继承而来,并添加了一些好用的方法,如j ...
- Python回顾笔记(此讲大致说明,详情请看之前的笔记)
内容概要 数据分析(numpy,pandas,matplib) 数据清洗 爬虫 teableau软件 今日内容概要 Python知识回顾 数据分析 ipython模块 anaconda软件 numpy ...
- JavaWeb-数据库基础
数据库基础 推荐阅读: 数据库:https://www.cnblogs.com/zwtblog/tag/数据库/ 数据库是学习JavaWeb的一个前置,只有了解了数据库的操作和使用,我们才能更好地组织 ...