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 应用,那么我推荐你使用上面的代码范例来实现手动的位移提交。

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

封面图

相关推荐
AAA修煤气灶刘哥9 分钟前
Kafka 入门不踩坑!从概念到搭环境,后端 er 看完就能用
大数据·后端·kafka
月小水长11 分钟前
大模型接入自定义 MCP Server,我开发了个免费使用的基金涨跌归纳和归因分析的 Agent
人工智能·后端
yinke小琪12 分钟前
说说hashCode() 和 equals() 之间的关系
java·后端·面试
花果山最Man的男人30 分钟前
@Autowired注解使用说明
后端
京东云开发者31 分钟前
如何秒级实现接口间“幂等”补偿:一款轻量级仿幂等数据校正处理辅助工具
后端
会飞的架狗师40 分钟前
【MySQL体系】第1篇:从MySQL架构原理到存储的解析
后端·mysql
用户83562907805140 分钟前
用Python高效处理Excel数据:Excel数据读取指南
后端·python
IT技术小密圈43 分钟前
图解系统设计: 五分钟从单体架构到微服务(上)
后端
mudtools1 小时前
.NET驾驭Word之力:COM组件二次开发全攻略之连接Word与创建你的第一个自动化文档
后端·c#