19 | CommitFailedException异常怎么处理?


Kafka 核心技术与实战

客户端实践及原理剖析

19 | CommitFailedException异常怎么处理?

CommitFailedException异常说明

所谓 CommitFailedException,顾名思义就是 Consumer 客户端在提交位移时出现了错误或异常,而且还是那种不可恢复的严重异常。

社区对这个异常的解释:

Commit cannot be completed since the group has already rebalanced and assigned the partitions to another member. This means that the time between subsequent calls to poll() was longer than the configured max.poll.interval.ms, which typically implies that the poll loop is spending too much time message processing. You can address this either by increasing max.poll.interval.ms or by reducing the maximum size of batches returned in poll() with max.poll.records.
由于组已经重新平衡并将分区分配给另一个成员,因此无法完成提交。这意味着后续调用 poll() 之间的时间比配置的 max.poll.interval.ms 长,这通常意味着轮询循环花费了太多时间处理消息。您可以通过增加 max.poll.interval.ms 或通过使用 max.poll.records 减少 poll() 返回的批次的最大大小来解决这个问题。

在后半部分,社区给出了两个相应的解决办法(即橙色字部分):

  • 增加期望的时间间隔 max.poll.interval.ms 参数值。
  • 减少 poll 方法一次性返回的消息数量,即减少 max.poll.records 参数值。

从源代码方面来说,CommitFailedException 异常通常发生在手动提交位移时,即用户显式调用 KafkaConsumer.commitSync() 方法时。

package org.apache.kafka.clients.consumer;

import org.apache.kafka.common.KafkaException;

/**
 * This exception is raised when an offset commit with {@link KafkaConsumer#commitSync()} fails
 * 当使用 {@link KafkaConsumer#commitSync()} 的偏移提交失败时会引发此异常
 * with an unrecoverable error. This can happen when a group rebalance completes before the commit
 * could be successfully applied. In this case, the commit cannot generally be retried because some
 * of the partitions may have already been assigned to another member in the group.
 */
public class CommitFailedException extends KafkaException {

    private static final long serialVersionUID = 1L;

    public CommitFailedException(final String message) {
        super(message);
    }

    public CommitFailedException() {
        super("Commit cannot be completed since the group has already " +
                "rebalanced and assigned the partitions to another member. This means that the time " +
                "between subsequent calls to poll() was longer than the configured max.poll.interval.ms, " +
                "which typically implies that the poll loop is spending too much time message processing. " +
                "You can address this either by increasing max.poll.interval.ms or by reducing the maximum " +
                "size of batches returned in poll() with max.poll.records.");
    }
}
典型的场景

从使用场景来说,有两种典型的场景可能遭遇该异常:

场景一:当消息处理的总时间超过预设的 max.poll.interval.ms 参数值时(即消息处理太慢),Kafka Consumer 端会抛出 CommitFailedException 异常。

只需要写一个 Consumer 程序,使用 KafkaConsumer.subscribe 方法随意订阅一个主题,之后设置 Consumer 端参数 max.poll.interval.ms=5 秒,最后在循环调用 KafkaConsumer.poll 方法之间,插入 Thread.sleep(6000) 和手动提交位移,就可以成功复现这个异常了。

Properties props = new Properties();
…
props.put("max.poll.interval.ms", 5000);
consumer.subscribe(Arrays.asList("test-topic"));
 
while (true) {
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(1));
    // 使用 Thread.sleep 模拟真实的消息处理逻辑
    Thread.sleep(6000L);
    consumer.commitSync();
}

如果要防止这种场景下抛出异常,需要简化消息处理逻辑。具体来说有 4 种方法:

  • 缩短单条消息处理的时间。 比如,之前下游系统消费一条消息的时间是 100 毫秒,优化之后成功地下降到 50 毫秒,那么此时 Consumer 端的 TPS 就提升了一倍。
  • 增加 Consumer 端允许下游系统消费一批消息的最大时长。 这取决于 Consumer 端参数 max.poll.interval.ms 的值。
  • 减少下游系统一次性消费的消息总数。 这取决于 Consumer 端参数 max.poll.records 的值。降低此参数值是避免 CommitFailedException 异常最简单的手段。
  • 下游系统使用多线程来加速消费。 这是“最高级”同时也是最难实现的解决办法。

场景二:如果应用中有设置相同 group.id 值的消费者组程序和独立消费者程序,那么当独立消费者程序手动提交位移时,Kafka 就会立即抛出 CommitFailedException 异常。

因为 Kafka 无法识别这个具有相同 group.id 的消费者实例,于是就向它返回一个错误,表明它不是消费者组内合法的成员。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

久违の欢喜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值