Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/apache-kafka/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Spring Kafka事务导致生产者每消息偏移量增加两倍_Java_Apache Kafka_Spring Kafka - Fatal编程技术网

Java Spring Kafka事务导致生产者每消息偏移量增加两倍

Java Spring Kafka事务导致生产者每消息偏移量增加两倍,java,apache-kafka,spring-kafka,Java,Apache Kafka,Spring Kafka,我在使用Spring(boot)Kafka的微服务中有一个消费-转换-生产工作流。我需要实现卡夫卡事务提供的一次完整的权杖。 下面是代码片段: 配置 @Bean public ProducerFactory<String, String> producerFactory() { Map<String, Object> props = new HashMap<>(); props.put(ProducerConfig.BOOTSTRAP_SERV

我在使用Spring(boot)Kafka的微服务中有一个消费-转换-生产工作流。我需要实现卡夫卡事务提供的一次完整的权杖。 下面是代码片段:

配置

@Bean
public ProducerFactory<String, String> producerFactory() {
    Map<String, Object> props = new HashMap<>();
    props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
    props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
    props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
    props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true);
    props.put(ProducerConfig.MAX_REQUEST_SIZE_CONFIG, 1024 * 1024);
    DefaultKafkaProducerFactory<String, String> defaultKafkaProducerFactory = new DefaultKafkaProducerFactory<>(props);
    defaultKafkaProducerFactory.setTransactionIdPrefix("kafka-trx-");
    return defaultKafkaProducerFactory;
}

@Bean
public ConsumerFactory<String, String> consumerFactory() {
    Map<String, Object> props = new HashMap<>();
    props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
    props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
    props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
    props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 5000);
    props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
    props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
    props.put(ConsumerConfig.ISOLATION_LEVEL_CONFIG, "read_committed");
    return new DefaultKafkaConsumerFactory<>(props);
}

@Bean
public KafkaTemplate<String, String> kafkaTemplate() {
    return new KafkaTemplate<>(producerFactory());
}

@Bean
public KafkaTransactionManager<String, String> kafkaTransactionManager() {
    return new KafkaTransactionManager<>(producerFactory());
}

@Bean
@Qualifier("chainedKafkaTransactionManager")
public ChainedKafkaTransactionManager<String, Object> chainedKafkaTransactionManager(KafkaTransactionManager<String, String> kafkaTransactionManager) {
    return new ChainedKafkaTransactionManager<>(kafkaTransactionManager);
}

@Bean
public ConcurrentKafkaListenerContainerFactory<?, ?> concurrentKafkaListenerContainerFactory(ChainedKafkaTransactionManager<String, Object> chainedKafkaTransactionManager) {
    ConcurrentKafkaListenerContainerFactory<String, String> concurrentKafkaListenerContainerFactory = new ConcurrentKafkaListenerContainerFactory<>();
    concurrentKafkaListenerContainerFactory.setConsumerFactory(consumerFactory());
    concurrentKafkaListenerContainerFactory.setBatchListener(true);
    concurrentKafkaListenerContainerFactory.setConcurrency(nexusConsumerConcurrency);
    //concurrentKafkaListenerContainerFactory.setReplyTemplate(kafkaTemplate());
    concurrentKafkaListenerContainerFactory.getContainerProperties().setAckMode(AbstractMessageListenerContainer.AckMode.BATCH);
    concurrentKafkaListenerContainerFactory.getContainerProperties().setTransactionManager(chainedKafkaTransactionManager);
    return concurrentKafkaListenerContainerFactory;
}
@Bean
公共生产工厂生产工厂(){
Map props=newhashmap();
put(ProducerConfig.BOOTSTRAP\u server\u CONFIG,bootstrapserver);
put(ProducerConfig.KEY\u SERIALIZER\u CLASS\u CONFIG,StringSerializer.CLASS);
put(ProducerConfig.VALUE\u SERIALIZER\u CLASS\u CONFIG,StringSerializer.CLASS);
props.put(ProducerConfig.ENABLE\u idemptence\u CONFIG,true);
props.put(ProducerConfig.MAX\u REQUEST\u SIZE\u CONFIG,1024*1024);
DefaultKafkaProducerFactory DefaultKafkaProducerFactory=新的DefaultKafkaProducerFactory(道具);
defaultKafkaProducerFactory.setTransactionIdPrefix(“kafka trx-”);
返回defaultKafkaProducerFactory;
}
@豆子
公共消费者工厂消费者工厂(){
Map props=newhashmap();
put(ConsumerConfig.BOOTSTRAP\u server\u CONFIG,bootstrapserver);
put(ConsumerConfig.KEY\u反序列化程序\u类\u配置,StringDeserializer.CLASS);
put(ConsumerConfig.VALUE\u反序列化程序\u类\u配置,StringDeserializer.CLASS);
props.put(ConsumerConfig.MAX\u POLL\u RECORDS\u CONFIG,5000);
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,“最早”);
put(ConsumerConfig.ENABLE\u AUTO\u COMMIT\u CONFIG,false);
put(ConsumerConfig.ISOLATION_LEVEL_CONFIG,“read_committed”);
返回新的默认卡夫卡消费工厂(道具);
}
@豆子
公共卡夫卡模板卡夫卡模板(){
返回新的卡夫卡模板(producerFactory());
}
@豆子
公共KafkaTransactionManager KafkaTransactionManager(){
返回新的KafkatTransactionManager(producerFactory());
}
@豆子
@限定词(“ChainedKafkatTransactionManager”)
公共链接KafkaTransactionManager链接KafkaTransactionManager(KafkaTransactionManager KafkaTransactionManager){
返回新的ChainedKafkaTransactionManager(kafkaTransactionManager);
}
@豆子
公共ConcurrentKafkalListenerContainerFactory ConcurrentKafkalListenerContainerFactory(链接KafkatTransactionManager链接KafkatTransactionManager){
ConcurrentKafkListenerContainerFactory ConcurrentKafkListenerContainerFactory=新的ConcurrentKafkListenerContainerFactory();
concurrentKafkaListenerContainerFactory.setConsumerFactory(consumerFactory());
concurrentKafkaListenerContainerFactory.setBatchListener(true);
concurrentKafkaListenerContainerFactory.setConcurrency(nexusConsumerConcurrency);
//concurrentKafkaListenerContainerFactory.setReplyTemplate(kafkaTemplate());
concurrentKafkaListenerContainerFactory.getContainerProperties().setAckMode(AbstractMessageListenerContainer.AckMode.BATCH);
ConcurrentKafkalListenerContainerFactory.getContainerProperties().setTransactionManager(ChainedKafkatTransactionManager);
返回concurrentKafkaListenerContainerFactory;
}
听众

@KafkaListener(topics = "${kafka.xxx.consumerTopic}", groupId = "${kafka.xxx.consumerGroup}", containerFactory = "concurrentKafkaListenerContainerFactory")
public void listen(@Payload List<String> msgs, @Header(KafkaHeaders.RECEIVED_PARTITION_ID) List<Integer> partitions, @Header(KafkaHeaders.OFFSET) List<Integer> offsets) {

    int i = -1;
    for (String msg : msgs) {
        ++i;
        LOGGER.debug("partition={}; offset={}; msg={}", partitions.get(i), offsets.get(i), msg);
        String json = transform(msg);
        kafkaTemplate.executeInTransaction(kt -> kt.send(producerTopic, json));
    }
}
@KafkaListener(topics=“${kafka.xxx.consumerTopic}”,groupId=“${kafka.xxx.consumerGroup}”,containerFactory=“concurrentKafkaListenerContainerFactory”)
public void listen(@Payload List msgs,@Header(KafkaHeaders.RECEIVED_partitions_ID)列表分区,@Header(KafkaHeaders.OFFSET)列表偏移){
int i=-1;
用于(字符串消息:msgs){
++一,;
debug(“partition={};offset={};msg={}”,partitions.get(i),offset.get(i),msg);
字符串json=transform(msg);
kafkaTemplate.executeInTransaction(kt->kt.send(producerTopic,json));
}
}
然而在产品环境中,我遇到了一个奇怪的问题。生产者发送的每条消息的偏移量增加2,消费者不提交消耗偏移量

消费者对主题1的补偿

主题1消费者详细信息

生产到主题2

但是,生产者发送的消息数与消耗的消息数相同。生产商下游可连续接收topic2的MSG。日志中未发现任何错误或异常

我想知道为什么consume transform Product workflow看起来还可以(正好有一次scemantics也得到了保证),但是consumed offset没有提交,并且生成的msg offset增量是2,而不是每个msg的1


如何修复它?谢谢

请注意自动提交设置。正如我看到的,你把它设为假:

props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);

因此,在这种情况下,您需要“手动”提交或将自动提交设置为true。

这就是它的设计方式。Kafka日志是不可变的,因此在事务结束时会使用额外的“插槽”来指示事务是提交还是回滚。这允许具有
read_committed
隔离级别的使用者跳过回滚事务

如果在一个事务中发布10条记录,您将看到偏移量增加11。如果您只发布一个,它将增加两个

如果希望发布参与使用者启动的事务(仅一次),则不应使用
executeInTransaction
;这将启动一个新事务

/**
 * Execute some arbitrary operation(s) on the operations and return the result.
 * The operations are invoked within a local transaction and do not participate
 * in a global transaction (if present).
 * @param callback the callback.
 * @param <T> the result type.
 * @return the result.
 * @since 1.1
 */
<T> T executeInTransaction(OperationsCallback<K, V, T> callback);


Kafka事务将在事务处理中提交偏移量。我不应该自己提交偏移量或启用自动提交。您不应该使用
executeInTransaction
。使用kafkaTemplate.send()仍然不会提交已使用的偏移量。如果不是send(),我应该调用哪个方法?当侦听器退出时,侦听器容器将消耗的偏移量(+1)发送到事务;打开提交日志记录,您将看到它。请看我答案的编辑。谢谢。我按照您所说的设置了提交日志级别。信息日志显示发送到事务的消耗的偏移量是正确的。可能由于kafka事务的某些机制,kafka monitor web ui无法正确显示conumser偏移量。
spring.kafka.producer.transaction-id-prefix=tx-
spring.kafka.consumer.properties.isolation.level=read_committed
spring.kafka.consumer.auto-offset-reset=earliest
foo0@56
2019-12-04 10:07:18.551  INFO 55430 --- [      foo-0-C-1] essageListenerContainer$ListenerConsumer : Sending offsets to transaction: {so59152915-1-0=OffsetAndMetadata{offset=57, leaderEpoch=null, metadata=''}}
foo1@57
FOO0
2019-12-04 10:07:18.558  INFO 55430 --- [      bar-0-C-1] essageListenerContainer$ListenerConsumer : Sending offsets to transaction: {so59152915-2-0=OffsetAndMetadata{offset=63, leaderEpoch=null, metadata=''}}
2019-12-04 10:07:20.562  INFO 55430 --- [      foo-0-C-1] essageListenerContainer$ListenerConsumer : Sending offsets to transaction: {so59152915-1-0=OffsetAndMetadata{offset=58, leaderEpoch=null, metadata=''}}
foo2@58