【问题标题】:What is reason for getting ProducerFencedException during producer.send?在 producer.send 期间获得 ProducerFencedException 的原因是什么?
【发布时间】:2019-04-03 03:57:34
【问题描述】:

尝试将大约 50K 消息加载到 KAFKA 主题中。在少数运行开始时低于异常但并非一直如此。

org.apache.kafka.common.KafkaException: Cannot execute transactional method because we are in an error state  
at org.apache.kafka.clients.producer.internals.TransactionManager.maybeFailWithError(TransactionManager.java:784) ~[kafka-clients-2.0.0.jar:?]  
at org.apache.kafka.clients.producer.internals.TransactionManager.beginAbort(TransactionManager.java:229) ~[kafka-clients-2.0.0.jar:?]  
at  org.apache.kafka.clients.producer.KafkaProducer.abortTransaction(KafkaProducer.java:679) ~[kafka-clients-2.0.0.jar:?]  
at myPackage.persistUpdatesPostAction(MyCode.java:??) ~[aKafka.jar:?]  
...  
Caused by: 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.  

代码块如下:

public void persistUpdatesPostAction(List<Message> messageList ) {
    if ((messageList == null) || (messageList.isEmpty())) {
        return;
    }
    logger.createDebug("Messages in batch(postAction) : "+ messageList.size());
    Producer<String,String> producer = KafkaUtils.getProducer(Thread.currentThread().getName());
    try {
        producer.beginTransaction();
        createKafkaBulkInsert1(producer, messageList, "Topic1");
        createKafkaBulkInsert2(producer, messageList, "Topic2");
        createKafkaBulkInsert3(producer, messageList, "Topic3");
        producer.commitTransaction();
    } catch (Exception e) {
        producer.abortTransaction();
        producer.close();
        KafkaUtils.removeProducer(Thread.currentThread().getName());
    }
}

-----------

static Properties setPropertiesProducer() {
    Properties temp = new Properties();
    temp.put("bootstrap.servers", "localhost:9092");
    temp.put("acks", "all");
    temp.put("retries", 1);
    temp.put("batch.size", 16384);
    temp.put("linger.ms", 5);
    temp.put("buffer.memory", 33554432);
    temp.put("key.serializer",   "org.apache.kafka.common.serialization.StringSerializer");
    temp.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
    return temp;
}

public static Producer<String, String> getProducer(String aThreadId) {
    if ((producerMap.size() == 0) || (producerMap.get(aThreadId) == null)) {
        Properties temp = producerProps;
        temp.put("transactional.id", aThreadId);
        Producer<String, String> producer = new KafkaProducer<String, String>(temp);
        producerMap.put(aThreadId, producer);
        producer.initTransactions();
        return producer;
    }
    return producerMap.get(aThreadId);
}

public static void removeProducer(String aThreadId) {
    logger.createDebug("Removing Thread ID :" + aThreadId);
    if (producerMap.get(aThreadId) == null)
        return;
    producerMap.remove(aThreadId);
}

【问题讨论】:

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


    【解决方案1】:

    引起:org.apache.kafka.common.errors.ProducerFencedException:Producer 尝试使用旧时代进行操作。要么有一个更新的生产者 相同的 transactionalId,或者生产者的交易已被 经纪人。

    此异常消息不是很有帮助。我相信 试图 说代理不再有客户端发送的事务 ID 的任何记录。这可能是因为:

    • 其他人正在使用相同的事务 ID 并已提交它。根据我的经验,除非您在客户端之间共享事务 ID,否则这种情况不太可能发生。我们使用 UUID.randomUUID() 确保我们的 ID 是唯一的。
    • 交易超时并被代理自动化删除。

    在我们的例子中,我们经常遇到事务超时,从而产生了这个异常。有 2 个属性可以控制代理在中止和忘记之前记住交易的时间。

    • transaction.max.timeout.ms -- 一个broker 属性,它指定事务被中止和遗忘之前的最大毫秒数。许多 Kafka 版本的默认值似乎是 900000(15 分钟)。 Documentation from Kafka 说:

      事务允许的最大超时。如果客户端请求的事务时间超过此时间,则代理将在 InitProducerIdRequest 中返回错误。这可以防止客户端超时时间过长,这可能会阻止消费者从事务中包含的主题中读取。

    • transaction.timeout.ms -- 一个生产者客户端 属性,用于设置创建事务时的超时时间(以毫秒为单位)。许多 Kafka 版本的默认值似乎是 60000(1 分钟)。来自 Kafka 的文档说:

      事务协调器在主动中止正在进行的事务之前等待来自生产者的事务状态更新的最长时间(毫秒)。

    如果客户端中设置的transaction.timeout.ms属性超过了broker中的transaction.max.timeout.ms属性,生产者会立即抛出类似如下异常:

    org.apache.kafka.common.KafkaException: Unexpected error in
    InitProducerIdResponse The transaction timeout is larger than the maximum value
    allowed by the broker (as configured by transaction.max.timeout.ms).
    

    【讨论】:

    • 谢谢,这个解释比官方文档好用!
    【解决方案2】:

    当运行应用程序的多个实例时,transactional.id 必须在所有实例上都相同才能满足围栏僵尸 在侦听器容器线程上生成记录。然而,当 使用由 a 启动的事务生成记录 侦听器容器,每个实例的前缀必须不同。

    https://docs.spring.io/spring-kafka/reference/html/#transaction-id-prefix

    【讨论】:

      【解决方案3】:

      我编写了一个单元测试来重现这一点,从这段 Java 代码中,您可以通过两个相同的 tansactional id 轻松理解这是如何发生的。

        @Test
        public void SendOffset_TwoProducerDuplicateTrxId_ThrowException() {
          // create two producer with same transactional id
          Producer producer1 = KafkaBuilder.buildProducer(trxId, servers);
          Producer producer2 = KafkaBuilder.buildProducer(trxId, servers);
      
          offsetMap.put(new TopicPartition(topic, 0), new OffsetAndMetadata(1000));
      
          // initial and start two transactions
          sendOffsetBegin(producer1);
          sendOffsetBegin(producer2);
      
          try {
            // when commit first transaction it expected to throw exception
            sendOffsetEnd(producer1);
      
            // it expects not run here
            Assert.assertTrue(false);
          } catch (Throwable t) {
            // it expects to catch the exception
            Assert.assertTrue(t instanceof ProducerFencedException);
          }
        }
      
        private void sendOffsetBegin(Producer producer) {
          producer.initTransactions();
          producer.beginTransaction();
          producer.sendOffsetsToTransaction(offsetMap, consumerGroup);
        }
      
        private void sendOffsetEnd(Producer producer) {
          producer.commitTransaction();
        }
      

      【讨论】:

        【解决方案4】:

        我的 Producer 初始化代码中存在竞争条件。我已通过将 Producer 映射更改为 ConcurrentHashMap 类型来确保线程安全。

        【讨论】:

        • ThreadLocal 会起作用吗?
        • @AshikaUmangaUmagiliya 可能是,没试过。
        猜你喜欢
        • 1970-01-01
        • 2017-04-10
        • 2014-02-12
        • 1970-01-01
        • 2017-07-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多