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

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

封面图

相关推荐
海绵波波10731 分钟前
flask后端开发(1):第一个Flask项目
后端·python·flask
AI人H哥会Java3 小时前
【Spring】控制反转(IoC)与依赖注入(DI)—IoC容器在系统中的位置
java·开发语言·spring boot·后端·spring
凡人的AI工具箱3 小时前
每天40分玩转Django:Django表单集
开发语言·数据库·后端·python·缓存·django
奔跑草-3 小时前
【数据库】SQL应该如何针对数据倾斜问题进行优化
数据库·后端·sql·ubuntu
中國移动丶移不动3 小时前
Java 并发编程:原子类(Atomic Classes)核心技术的深度解析
java·后端
Q_19284999065 小时前
基于Spring Boot的旅游推荐系统
spring boot·后端·旅游
愤怒的代码5 小时前
Spring Boot对访问密钥加密解密——RSA
java·spring boot·后端
美美的海顿5 小时前
springboot基于Java的校园导航微信小程序的设计与实现
java·数据库·spring boot·后端·spring·微信小程序·毕业设计
愤怒的代码5 小时前
Spring Boot中幂等性的应用
java·spring boot·后端
xiaocaibao7776 小时前
编程语言的软件工程
开发语言·后端·golang