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> ...