kafka提交异常org.apache.kafka.clients.consumer.CommitFailedException

在测试kafka 0.10.1.0版本的kafkaConsumer时遇到CommitFailedException异常,原因是手动提交时因max.poll.interval.ms参数设置不当导致消费者组重新平衡。解决方案是调整max.poll.interval.ms值,确保它大于处理消息所需的最大时间,或者根据消息处理速度来设定。修改后问题得到解决。

一、背景

       kafka使用版本为0.10.1.0,今天在测试kafkaConsumer的时候,代码如下

public static void main(String[] args) {
        Properties props = new Properties();
        props.put("bootstrap.servers", "192.168.80.132:9092");
        props.put("group.id", "testId");
        props.put("enable.auto.commit", "false");
        props.put("session.timeout.ms", "10000");
        props.put("max.poll.interval.ms", "3000");
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        consumer.subscribe(Arrays.asList("kafka-topic-02"));
        try {
            while(true) {
                ConsumerRecords<String, String> records = consumer.poll(100);
                System.out.println("*********开始休眠6s,模拟消息处理时间**********");
                Thread.sleep(6000);
                System.out.println("***************休眠结束*****************");
                consumer.commitSync();
            }
        }catch(Exception e) {
            e.printStackTrace();
        }finally {
            consumer.close();
        }

    一运行,报错了,报错如下,意思是我们手动提交的时候失败了,因为消费者组开启了rebalanced操作且将对应的分区分配给了其它消费者

*********开始休眠6s,模拟消息处理时间**********
***************休眠结束*****************
org.apache.kafka.clients.consumer.CommitFailedException: 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 the session timeout or by reducing the maximum size of batches returned in poll() with max.poll.records.
    at org.apache.kafka.clients.consumer.internals.ConsumerCoordinator.sendOffsetCommitRequest(ConsumerCoordinator.java:600)
    at org.apache.kafka.clients.consumer.internals.ConsumerCoordinator.commitOffsetsSync(ConsumerCoordinator.java:498)
    at org.apache.kafka.clients.consumer.KafkaConsumer.commitSync(KafkaConsumer.java:1104)
    at org.apache.kafka.clients.consumer.KafkaConsumer.commitSync(KafkaConsumer.java:1072)
    at com.tanjie.kafka.ConsumerDemo.main(ConsumerDemo.java:29)

      经过查看官网,发现参数max.poll.interval.ms(官网给得默认值为3000)的意思为,当我们从kafkaServer端poll消息时,poll()的调用之间的最大延迟。 这提供了消费者在获取更多记录之前可以空闲的时间量的上限。 如果在此超时到期之前未调用poll(),则认为使用者失败,并且消费者组将重新平衡以便将分区重新分配给其他消费者,而恰好这里我们设置了Thread.sleep(6000) > max.poll.interval.ms值,也就是我们在手动提交的时候,实际上分区信息已经被分配到整个消费者组里面的其它消费者了。

     了解了上面的原因,修改也很简单,第一是增大max.poll.interval.ms的值,不过我们在实际的生产中,可以测试一下,假如你每次poll 100条消息,每条消息处理N/s 那么最好将max.poll.interval.ms值设置为 > 100*N,这样就不会出现上面的异常了。

     经过修改后代码如下

public static void main(String[] args) {
		Properties props = new Properties();
		props.put("bootstrap.servers", "192.168.80.132:9092");
		props.put("group.id", "testId");
		props.put("enable.auto.commit", "false");
		props.put("session.timeout.ms", "10000");
		props.put("max.poll.interval.ms", "3000");
		props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
		props.put("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
		KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
		consumer.subscribe(Arrays.asList("kafka-topic-02"));
		try {
			while(true) {
				ConsumerRecords<String, String> records = consumer.poll(100);
				System.out.println("*********开始休眠2s,模拟消息处理时间**********");
				Thread.sleep(2000);
				System.out.println("***************休眠结束*****************");
				for(ConsumerRecord<String,String> record : records) {
					System.out.println("offest:" + record.offset() + ";value: " + record.value());
				}
				consumer.commitSync();
			}
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			consumer.close();
		}
	}

     运行结果如下:

*********开始休眠2s,模拟消息处理时间**********
***************休眠结束*****************
*********开始休眠2s,模拟消息处理时间**********
***************休眠结束*****************
offest:18;value: 3f85df8b-4ec6-4b9d-be22-0c269ead55a9
offest:19;value: 34d8b20d-eb24-4cd5-a249-75b41a6fda5b
offest:20;value: dcc4bd2f-912d-4fda-b141-7c759ea3dc24
offest:21;value: d9a7db66-d9b5-4c0f-9287-f0197a7dcdf0
offest:14;value: 2dfab504-da05-4f38-a7be-cb1e0c7a8ea9
offest:15;value: c3170f1b-bbe6-4c58-8fda-c7a548ca4e3c
offest:18;value: 966faa1c-4cf1-44cd-a63e-818940fcb26a
offest:19;value: 7bb5b5aa-bd74-4c2c-9008-58714028a882
offest:20;value: 0f79cacf-c558-4696-985c-0ea0949acd4e
offest:21;value: 471f54a0-529f-4f54-b9fb-706c042839e2
*********开始休眠2s,模拟消息处理时间**********
***************休眠结束*****************
*********开始休眠2s,模拟消息处理时间**********
***************休眠结束*****************
*********开始休眠2s,模拟消息处理时间**********
### Kafka消费者OffsetOutOfRangeException异常解决方案 Kafka消费者遇到`OffsetOutOfRangeException`异常通常是因为消费者的偏移量(offset)超出了Kafka Broker中实际可用的消息范围。这种情况下,可能的原因包括Kafka Broker的日志保留策略导致消息被删除,或者消费者尝试读取不存在的偏移位置。 #### 1. **原因分析** 当Kafka Broker根据日志保留策略清理旧数据时,如果消费者未能及时消费这些数据,则可能导致存储在ZooKeeper或Kafka内部的消费者偏移量指向已删除的消息区域[^1]。此时,再次启动消费者程序会触发`OffsetOutOfRangeException`异常。 此外,在某些场景下,分区的领导节点发生变化后,消费者的当前偏移量可能不再有效。这种情况可以通过比较`FetchState`中的`position`字段与最新的`LeaderAndEpoch`来检测并修复[^2]。 --- #### 2. **解决方案** ##### 方法一:调整Kafka Broker的日志保留时间 通过修改Kafka Broker的配置参数`log.retention.hours`或`log.retention.minutes`,延长日志保留周期以减少因数据过期而导致的异常风险。例如: ```properties log.retention.minutes=10 ``` 完成配置更改后需重启Kafka服务使设置生效。随后可通过测试验证此改动是否能够缓解问题。 ##### 方法二:重置消费者组的偏移量 利用命令行工具重新设定消费者组的起始偏移点至最新或最早的有效位置之一。具体操作如下所示: ```bash kafka-consumer-groups.sh --bootstrap-server localhost:9092 \ --group my-group-name \ --reset-offsets --to-latest --execute --all-topics ``` 上述脚本将指定分组的所有主题订阅均更新为其各自现存记录集合末端处的新起点;同样也可以选择其他选项比如`--to-datetime` 或者 `--shift-by` 来实现更精细控制[^3]。 ##### 方法三:增强应用程序逻辑处理机制 对于基于Direct Stream API构建的应用而言,应当考虑引入额外错误恢复流程以便应对突发状况下的不可预期行为模式。一种常见做法是在捕获到此类特定类型的运行时期间抛出事件之后主动查询目标Topic对应Partition上的现有边界值(`earliest_offset`, `latest_offset`) 并据此决定如何修正受影响实例的状态信息[^4]: ```python from kafka import KafkaConsumer, TopicPartition consumer = KafkaConsumer(bootstrap_servers='localhost:9092') tp = TopicPartition('my-topic', partition_id) # 获取最早的和最后的偏移量 earliest_offsets = consumer.beginning_offsets([tp]) latest_offsets = consumer.end_offsets([tp]) if stored_offset < earliest_offsets[tp]: # 如果存储的偏移小于最早的偏移,则将其设为最早的偏移 new_offset = earliest_offsets[tp] else: new_offset = min(stored_offset, latest_offsets[tp]) consumer.seek(tp, new_offset) ``` 以上代码片段展示了怎样动态计算合适的替代方案从而避免潜在崩溃隐患的同时维持正常业务功能运转连续性。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值