Kafka 位移提交的正确姿势

你说你 Kafka 用了很多年了,但是位移提交的这些细节你未必清楚。

不久前有个同事出去面试了,他说在 Kafka 问题上被面试官藐视了。

同事:我做了一个消息总线,简化了业务团队消息的收发,并且提供了消息管理功能。

面试官:你做的这个东西,有什么深入价值吗?

同事:目的在于封装技术的复杂度,使业务团队更加专注于业务。

面试官:这个价值并没有真实打动我,这不是很简单吗?

简单吗?那我问你:Kafka 是如何进行位移提交的?(你不一定能答得全面)

回到问题本身,Kafka 位移提交的方式有两种:有自动提交和手动提交。

一、自动提交

在默认情况下,Consumer 每 5 秒自动提交一次位移。现在,我们假设提交位移之后的 3 秒发生了 Rebalance 操作。在 Rebalance 之后,所有 Consumer 从上一次提交的位移处继续消费,但该位移已经是 3 秒前的位移数据了,故在 Rebalance 发生前 3 秒消费的所有数据都要重新再消费一次。

虽然你能够通过减少 auto.commit.interval.ms 的值来提高提交频率,但这么做只能缩小重复消费的时间窗口,不可能完全消除它。这是自动提交机制的一个缺陷。

弥补这个问题,就要用到手动提交了。

二、手动同步提交

下面这段代码展示了 commitSync() 的使用方法。

scss 复制代码
while (true) {
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(1));
    process(records); // 处理消息
    try {
        consumer.commitSync();
    } catch (CommitFailedException e) {
        handle(e); // 处理提交失败异常
    }
}

手动提交位移的好处就在于更加灵活,你完全能够把控位移提交的时机和频率,可以在消费完成后立马提交位移。

但是,它也有一个缺陷,就是在调用 commitSync() 时,Consumer 程序会处于阻塞状态,直到远端的 Broker 返回提交结果,这个状态才会结束。在任何系统中,因为程序而非资源限制而导致的阻塞都可能是系统的瓶颈,会影响整个应用程序的 TPS。

三、手动异步提交

由于它是异步的,Kafka 提供了回调函数(callback),供你实现提交之后的逻辑,比如记录日志或处理异常等。下面这段代码展示了调用 commitAsync() 的方法。

scss 复制代码
while (true) {
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(1));
    process(records); // 处理消息
    consumer.commitAsync((offsets, exception) -> {
        if (exception != null) {
            handle(exception);
        }
    });
}

commitAsync 的问题在于,出现问题时它不会自动重试。因为它是异步操作,倘若提交失败后自动重试,那么它重试时提交的位移值可能早已经"过期"或不是最新值了。因此,异步提交的重试其实没有意义,所以 commitAsync 是不会重试的。

那同步提交、异步提交那我该怎么选呢?

小孩子才做选择题,成年人当然是全部都要啊!

四、手动同异步结合提交

下面这段代码,将两个 API 方法结合使用进行手动提交。

scss 复制代码
try {
    while (true) {
        ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(1));
        process(records); // 处理消息
        consumer.commitAysnc(); // 使用异步提交规避阻塞
    }
} catch (Exception e) {
    handle(e); // 处理异常
} finally {
    try {
        consumer.commitSync(); // 最后一次提交使用同步阻塞式提交
    } finally {
        consumer.close();
    }
}

对于常规性、阶段性的手动提交,我们调用 commitAsync() 避免程序阻塞,而在 Consumer 要关闭前,我们调用 commitSync() 方法执行同步阻塞式的位移提交,以确保 Consumer 关闭前能够保存正确的位移数据。将两者结合后,我们既实现了异步无阻塞式的位移管理,也确保了 Consumer 位移的正确性。

所以,如果你需要自行编写代码开发一套 Kafka Consumer 应用,那么我推荐你使用上面的代码范例来实现手动的位移提交。

(看到这里,面试官陷入了深深的沉默中...)

封面图

相关推荐
BYSJMG7 分钟前
计算机毕设推荐:基于大数据的共享单车数据可视化分析
大数据·后端·python·信息可视化·数据分析·课程设计
短剑重铸之日18 分钟前
《设计模式》第九篇:三大类型之结构型模式
java·后端·设计模式·组合模式·代理模式·结构性模式
树码小子23 分钟前
SpringIoC & DI (4)DI详解(三种注入方式)
java·后端·spring·ioc
人道领域1 小时前
SSM从入门到入土(Spring Bean实例化与依赖注入全解析)
java·开发语言·spring boot·后端
long3161 小时前
Z算法(线性时间模式搜索算法)
java·数据结构·spring boot·后端·算法·排序算法
小楼v1 小时前
如何实现AI生成应用部署功能
java·后端·ai·部署
李慕婉学姐1 小时前
Springboot眼镜店管理系统ferchy1l(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
暴躁小师兄数据学院2 小时前
【WEB3.0零基础转行笔记】Golang编程篇-第4讲:Go语言中的流程控制
开发语言·后端·golang·web3·区块链
Honmaple2 小时前
openclaw 钉钉 Webhook 完全指南
后端
短剑重铸之日2 小时前
《设计模式》第十篇:三大类型之行为型模式
java·后端·设计模式·责任链模式·访问者模式·行为型模式