使用Kafka Spout的Apache风暴给出了错误:IllegalStateException

时间:2018-12-03 09:47:20

标签: apache-kafka tuples apache-storm

Version Info: 
   "org.apache.storm" % "storm-core" % "1.2.1" 
   "org.apache.storm" % "storm-kafka-client" % "1.2.1" 

我有一个如下的风暴拓扑:

  

螺栓A->螺栓B->螺栓C->螺栓D

boltA只是对请求进行某种格式设置并发出另一个元组。 boltB进行一些处理,并为每个接受的元组发出大约100个元组。 boltCboltD处理这些元组。所有螺栓都实现BaseBasicBolt

我要注意的是,每当boltD将某些tuple标记为失败并通过抛出FailedException标记为重试时,比拓扑超时少了几分钟之后,出现以下错误:

2018-11-30T20:01:05.261+05:30 util [ERROR] Async loop died!
java.lang.IllegalStateException: Attempting to emit a message that has already been committed. This should never occur when using the at-least-once processing guarantee.
        at org.apache.storm.kafka.spout.KafkaSpout.emitOrRetryTuple(KafkaSpout.java:471) ~[stormjar.jar:?]
        at org.apache.storm.kafka.spout.KafkaSpout.emitIfWaitingNotEmitted(KafkaSpout.java:440) ~[stormjar.jar:?]
        at org.apache.storm.kafka.spout.KafkaSpout.nextTuple(KafkaSpout.java:308) ~[stormjar.jar:?]
        at org.apache.storm.daemon.executor$fn__4975$fn__4990$fn__5021.invoke(executor.clj:654) ~[storm-core-1.2.1.jar:1.2.1]
        at org.apache.storm.util$async_loop$fn__557.invoke(util.clj:484) [storm-core-1.2.1.jar:1.2.1]
        at clojure.lang.AFn.run(AFn.java:22) [clojure-1.7.0.jar:?]
        at java.lang.Thread.run(Thread.java:745) [?:1.8.0_60]
2018-11-30T20:01:05.262+05:30 executor [ERROR]
java.lang.IllegalStateException: Attempting to emit a message that has already been committed. This should never occur when using the at-least-once processing guarantee.
        at org.apache.storm.kafka.spout.KafkaSpout.emitOrRetryTuple(KafkaSpout.java:471) ~[stormjar.jar:?]
        at org.apache.storm.kafka.spout.KafkaSpout.emitIfWaitingNotEmitted(KafkaSpout.java:440) ~[stormjar.jar:?]
        at org.apache.storm.kafka.spout.KafkaSpout.nextTuple(KafkaSpout.java:308) ~[stormjar.jar:?]
        at org.apache.storm.daemon.executor$fn__4975$fn__4990$fn__5021.invoke(executor.clj:654) ~[storm-core-1.2.1.jar:1.2.1]
        at org.apache.storm.util$async_loop$fn__557.invoke(util.clj:484) [storm-core-1.2.1.jar:1.2.1]
        at clojure.lang.AFn.run(AFn.java:22) [clojure-1.7.0.jar:?]
        at java.lang.Thread.run(Thread.java:745) [?:1.8.0_60]

似乎发生了这种情况,当boltB发出1个元组中的100个,而boltD失败了这100个元组中的一个时,我得到了这个错误。无法理解如何解决此问题,理想情况下,当所有100个元组都为ack时,它应该acked个原始元组,但是可能在所有这100个元组为{{ 1}},导致此错误。

编辑:

我可以用两个螺栓在以下拓扑结构上重现此信息,在集群模式下运行约5分钟后,它就会被重现:

BoltA

acked

螺栓B

acked

KafkaSpout:

case class Abc(index: Int, rand: Boolean)

class BoltA  extends BaseBasicBolt {

  override def execute(input: Tuple, collector: BasicOutputCollector): Unit = {
    val inp = input.getBinaryByField("value").getObj[someObj]
    val randomGenerator = new Random()

    var i = 0
    val rand = randomGenerator.nextBoolean()
    1 to 100 foreach {
      collector.emit(new Values(Abc(i, rand).getJsonBytes))
      i += 1
    }
  }

  override def declareOutputFields(declarer: OutputFieldsDeclarer): Unit = {
    declarer.declare(new Fields("boltAout"))
  }

}

其他配置:

messageTimeoutInSecons:300

enter image description here

1 个答案:

答案 0 :(得分:0)

此修复程序由@ Stig Rohde Døssing here提供。 here对该问题的确切原因进行了如下描述:

  

在针对STORM-2666和后续版本的修复程序中,我们添加了逻辑以处理以下情况:在已确认以下偏移量后,喷口接收到偏移量的确认。问题在于,喷口可能会提交所有确认的偏移量,但不能向前调整消费者位置,或者无法正确清除waitingToEmit。如果确认的偏移量远远超出了日志结束偏移量,则喷口可能最终会轮询其已提交的偏移量。

     

此修复程序略有错误。当使用者位置下降到承诺的偏移量之后时,我们确保向前调整位置,并清除承诺的偏移量之后的所有waitingToEmit消息。除非我们调整消费者位置,否则我们不会清除waitingToEmit,这确实是一个问题。

     

例如,假设偏移量1失败,偏移量2-10被确认,maxPollRecords为10。假设卡夫卡中有11条记录(1-11)。如果喷口向后偏移1,以重播它,则它将在民意测验中从消费者那里获得偏移1-10。使用者位置现在为11。喷口发出偏移1。说立即被确认。在下一次民意测验中,喷口将提交偏移量1-10,并检查是否应调整使用者位置和waitingToEmit。由于位置(11)在提交的偏移量(10)之前,因此不会清除waitingToEmit。由于waitingToEmit仍包含与上一个轮询相比的偏移量2-10,因此喷嘴将再次发出这些元组。

一个人可以看到修补程序here