如何在交易中发送由KafkaTemplate创建的Kafka偏移量?

时间:2019-07-18 10:54:31

标签: java apache-kafka transactions spring-kafka kafka-producer-api

我想实现read-process-write模式- https://www.confluent.io/blog/transactions-apache-kafka/。因此,我需要使用记录,进行处理,然后提交消耗的偏移量。

我使用org.apache.kafka.clients.consumer.KafkaConsumer来接收消息。我的意思是,它不是与春季相关的消费者。

我使用org.springframework.kafka.core.KafkaTemplate生成消息。我这样创建它的bean:

@Bean
public Map<String, Object> producerConfigs() {
    final 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.CLIENT_ID_CONFIG, UUID.randomUUID().toString());
    props.put(ProducerConfig.ACKS_CONFIG, "all");
    return props;
}

@Bean
public DefaultKafkaProducerFactory<String, String> defaultKafkaProducerFactory() {
    DefaultKafkaProducerFactory<String, String> kafkaProducerFactory = new DefaultKafkaProducerFactory<>(producerConfigs());
    kafkaProducerFactory.setTransactionIdPrefix("transaction-id-prefix");
    return kafkaProducerFactory;
}

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

我产生这样的结果消息:

ConsumerRecords<String, String> consumerRecords = consumer.poll(Duration.ofMillis(POLL_INTERVAL_IN_MS));

List<List<String>> outputMessages = produceOutput(consumerRecords);

kafkaTemplate.executeInTransaction(kafkaProducer -> {
    for (List<String> resultTasks : outputMessages) {
        for (String resultTask : resultTasks) {
            kafkaProducer.send("topic", "key", resultTask);
        }
    }

    kafkaProducer.sendOffsetsToTransaction(getOffsetsForCommit(consumerRecords), "consumerGroupId");
    return true;
});

最后,我遇到此错误:

java.lang.IllegalArgumentException: No transaction in process
    at org.springframework.util.Assert.isTrue(Assert.java:118)
    at org.springframework.kafka.core.KafkaTemplate.sendOffsetsToTransaction(KafkaTemplate.java:345)

此方法引发异常:

@Override
public void sendOffsetsToTransaction(Map<TopicPartition, OffsetAndMetadata> offsets, String consumerGroupId) {
    @SuppressWarnings("unchecked")
    KafkaResourceHolder<K, V> resourceHolder = (KafkaResourceHolder<K, V>) TransactionSynchronizationManager
            .getResource(this.producerFactory);
    Assert.isTrue(resourceHolder != null, "No transaction in process"); // here
    if (resourceHolder.getProducer() != null) {
        resourceHolder.getProducer().sendOffsetsToTransaction(offsets, consumerGroupId);
    }
}

那么,如何正确提交这些偏移量?

1 个答案:

答案 0 :(得分:1)

这是一个错误; sendOffsetsToTransaction()executeInTransaction中不起作用-它假定将Spring事务绑定到线程。

作为一种解决方法,您可以在方法上使用@Transactional或将事务模板与KafkaTransactionManager一起使用以启动Spring事务,而不是使用executeInTransaction()

TransactionTemplate tt = new TransactionTemplate(tm);

...

        this.tt.execute(s -> {
                    template.send(...);
                    template.sendOffsetsToTransaction(...);
                    return null;
                });

请打开GitHub Issue,我们将解决此问题。