消费者位移的提交方式以及提交时机须要根据不一样的业务场景进行选择,能够看以前的博客kafka消费者相关。 这里只作应用相关,更多的使用场景,该怎么用、什么时候用要看前面的博客了解原理。java
参考博客:https://blog.csdn.net/yy756127197/article/details/103895810bootstrap
自动提交偏移量
// 自动提交偏移量 // 若是设置成true,偏移量由auto.commit.interval.ms控制自动提交的频率 // 若是设置成false,不须要定时的提交offset,能够本身控制offset,当消息认为已消费过了,这个时候再去提交它们的偏移量。 // 这个颇有用的,当消费的消息结合了一些处理逻辑,这个消息就不该该认为是已经消费的,直到它完成了整个处理。 configProps.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true"); // 自动提交的频率 configProps.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");
手动提交偏移量
主要步骤: 1.消费者配置 configProps.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, “false”); 2.消费者配置ack模式factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE); 3.消费者手动提交 consumer.commitSync();异步
ConsumerConfig
@Configuration @EnableKafka public class ManualConsumerConfig { @Value("${kafka.bootstrap-servers}") private String bootstrapServers; @Value("${kafka.topic.manual}") private String topic; @Bean public KafkaListenerContainerFactory<?> manualKafkaListenerContainerFactory() { Map<String, Object> configProps = new HashMap<>(); configProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); configProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); configProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); configProps.put(ConsumerConfig.GROUP_ID_CONFIG, "manual-group"); // 手动提交 configProps.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false"); ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>(); factory.setConsumerFactory(new DefaultKafkaConsumerFactory<>(configProps)); // ack模式,详细见下文注释 factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE); return factory; } /** * AckMode针对ENABLE_AUTO_COMMIT_CONFIG=false时生效,有如下几种: * * RECORD * 每处理一条commit一次 * * BATCH(默认) * 每次poll的时候批量提交一次,频率取决于每次poll的调用频率 * * TIME * 每次间隔ackTime的时间去commit(跟auto commit interval有什么区别呢?) * * COUNT * 累积达到ackCount次的ack去commit * * COUNT_TIME * ackTime或ackCount哪一个条件先知足,就commit * * MANUAL * listener负责ack,可是背后也是批量上去 * * MANUAL_IMMEDIATE * listner负责ack,每调用一次,就当即commit * */ }
Consumer
@Component @Slf4j public class ManualConsumer { @KafkaListener(topics = "${kafka.topic.manual}", containerFactory = "manualKafkaListenerContainerFactory") public void receive(@Payload String message, @Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic, Consumer consumer, Acknowledgment ack) { System.out.println(String.format("From partition %d : %s", partition, message)); // 同步提交 consumer.commitSync(); // ack这种方式提交也能够 // ack.acknowledge(); } /** * commitSync和commitAsync组合使用 * <p> * 手工提交异步 consumer.commitAsync(); * 手工同步提交 consumer.commitSync() * <p> * commitSync()方法提交最后一个偏移量。在成功提交或碰到无怯恢复的错误以前, * commitSync()会一直重试,可是commitAsync()不会。 * <p> * 通常状况下,针对偶尔出现的提交失败,不进行重试不会有太大问题,由于若是提交失败是由于临时问题致使的, * 那么后续的提交总会有成功的。但若是这是发生在关闭消费者或再均衡前的最后一次提交,就要确保可以提交成功。 * 所以,在消费者关闭前通常会组合使用commitAsync()和commitSync()。 */ // @KafkaListener(topics = "${kafka.topic.manual}", containerFactory = "manualKafkaListenerContainerFactory") public void manual(@Payload String message, @Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic, Consumer consumer, Acknowledgment ack) { try { System.out.println(String.format("From partition %d : %s", partition, message)); // 同步提交 consumer.commitSync(); } catch (Exception e) { System.out.println("commit failed"); } finally { try { consumer.commitSync(); } finally { consumer.close(); } } } /** * 手动提交,指定偏移量 * * @param record * @param consumer */ // @KafkaListener(topics = "${kafka.topic.manual}", containerFactory = "manualKafkaListenerContainerFactory") public void offset(ConsumerRecord record, Consumer consumer) { System.out.println(String.format("From partition %d : %s", record.partition(), record.value())); Map<TopicPartition, OffsetAndMetadata> currentOffset = new HashMap<>(); currentOffset.put(new TopicPartition(record.topic(), record.partition()), new OffsetAndMetadata(record.offset() + 1)); consumer.commitSync(currentOffset); } }