Kafka消费者组重新平衡后的ProducerFencedException

时间:2020-04-21 14:22:01

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

我无法对类似主题发表评论: TransactionId prefix for producer-only and read-process-write - ProducerFencedException 所以我会问一个新问题。

用例:

  • 一个带有2个分区的主题
  • 使用者组“ sample-consumer-group”中并发= 1(默认值)的Spring @KafkaListener
  • 同一应用程序的两个实例-都具有相同的“ transaction-id-prefix”

1)我启动了第一个应用程序实例(我们称其为“ instance1”-一切正常-单个使用者同时订阅了两个分区。日志:

o.s.k.l.KafkaMessageListenerContainer : sample-consumer-group: partitions assigned: [sampleTopic-1, sampleTopic-0]

2)我启动第二个应用程序实例(instance2)-一切正常-从该实例登录:

o.s.k.l.KafkaMessageListenerContainer : sample-consumer-group: partitions assigned: [sampleTopic-1]

“ instance1”中的日志:

o.s.k.l.KafkaMessageListenerContainer    : sample-consumer-group: partitions revoked: [sampleTopic-1, sampleTopic-0]
o.s.k.l.KafkaMessageListenerContainer    : sample-consumer-group: partitions assigned: [sampleTopic-0]

还是可以的... 但是,当我然后尝试将消息发送到任何其他主题(不是从任何kafkaListener而是从某些@Transactional方法发送)时,会发生以下错误:

ERROR 4395 --- [roducer-tx-prefix-0] o.a.k.clients.producer.internals.Sender  : [Producer clientId=producer-tx-prefix-0, transactionalId=tx-prefix-0] Aborting producer batches due to fatal error

org.apache.kafka.common.errors.ProducerFencedException: Producer attempted an operation with an old epoch. Either there is a newer producer with the same transactionalId, or the producer's transaction has been expired by the broker.

ERROR 4395 --- [roducer-tx-prefix-0] o.s.k.support.LoggingProducerListener    : Exception thrown when sending a message with key='sync-register' and payload='2020-04-21T13:52:12.148412Z' to topic anotherTopic

那么,与仅生产者专用事务的每个实例都应该有单独的transaction-id-prefix的问题有关吗?如果是,当前状态是什么?如何在不使用单独的kafkaTemplate进行消费者启动和生产者启动的交易的情况下实现此目标?

1 个答案:

答案 0 :(得分:1)

请参见the documentation

如概述中所述,使用此属性配置生产者工厂,以构建生产者transactional.id属性。指定此属性时,存在很大的二分法,因为在运行应用程序的多个实例时,在侦听器容器线程上生成记录时,所有实例上的属性必须相同才能满足隔离僵尸(也在概述中提到)。但是,当使用未由侦听器容器启动的事务来生成记录时,每个实例上的前缀必须不同。 2.3版使配置更简单,尤其是在Spring Boot应用程序中。在以前的版本中,您必须创建两个生产者工厂和KafkaTemplate s-一个用于在侦听器容器线程上生成记录,一个用于由kafkaTemplate.executeInTransaction()或@Transactional方法上的事务拦截器启动的独立事务。 / p>

现在,您可以在KafkaTemplate和KafkaTransactionManager上覆盖工厂的transactionalIdPrefix。

在将事务管理器和模板用于侦听器容器时,通常将其保留为生产者工厂的默认属性。对于所有应用程序实例,该值应相同。对于由模板(或@Transaction的事务管理器)启动的事务,应分别在模板和事务管理器上设置属性。该属性在每个应用程序实例上必须具有不同的值。