消息队列-kafka关于CommitFailedException问题

931 阅读2分钟

概述

提交offset失败这个异常也是我们开发中常见的异常,那么这个异常是怎么引起的呢?只要我们明白原因,那么就能避免,特别是在异步提交情况下(offset丢失),导致重复消费,消息一直卡在最后一批,导致业务数据受到严重的破坏。

了解消费者实例机制

1.kafka消费者都是按照批量消息拿过来消费的,因为发送也是批量的,这样可以提高kafka性能

2.kafka中消费者有个参数max.poll.interval.ms(获取下一批消息间隔时间),如果上一批还没在这个事件之内处理完,那么kafka认为当前分配到当前分区的消费者实例一定出现了故障,为了不影响当前分区消费,将当前消费者实例剔除

3.kafka将触发rebalance,重新为当前分区分配新的消费者实例,这样保证分区能正常被消费

4.我们只要根据我们业务正常的设置max.poll.interval.ms,来评估我们消费一批数据时间即可避免

解决方案

1.设置一批数据能在max.poll.interval.ms处理完。比如一条消息需要处理1000ms,当前一批数据最多是10条,那么你只要将max.poll.interval.ms设置大于10000ms即可

2.设置一批数据最大数量(max.poll.records)。比如max.poll.interval.ms是11000ms,处理一条数据需要1000ms,那么你将max.poll.records设置为10即可

案例

发生CommitFailedException

//分区有8条数据,每条数据消费1000ms,max.poll.interval.ms=5000
@KafkaHandler
    @KafkaListener(topics = "quickstart-events",groupId = "test-consumer-group-2",
            properties = {"max.poll.interval.ms=5000"},concurrency = "1")
    public void test9(ConsumerRecord record, Acknowledgment ack){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("接收消息:"+String.format(
                "(消息体)%s--(分区)%s--(消费者实例)%s",record.value(),record.partition(),Thread.currentThread().getName()));
        ack.acknowledge();
    }

解决CommitFailedException

//设置max.poll.records=4,保证在max.poll.interval.ms=5000之内处理完
@KafkaHandler
    @KafkaListener(topics = "quickstart-events",groupId = "test-consumer-group-2",
            properties = {"max.poll.interval.ms=5000","max.poll.records=4"},concurrency = "1")
    public void test9(ConsumerRecord record, Acknowledgment ack){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("接收消息:"+String.format(
                "(消息体)%s--(分区)%s--(消费者实例)%s",record.value(),record.partition(),Thread.currentThread().getName()));
        ack.acknowledge();
    }