使用Kafka Streams在输出中设置时间戳无法进行转换

时间:2018-11-06 09:42:35

标签: apache-kafka apache-kafka-streams kafka-streams-scala

假设我们有一个变压器(用Scala编写)

new Transformer[String, V, (String, V)]() {
  var context: ProcessorContext = _

  override def init(context: ProcessorContext): Unit = {
    this.context = context
  }

  override def transform(key: String, value: V): (String, V) = {
    val timestamp = toTimestamp(value)
    context.forward(key, value, To.all().withTimestamp(timestamp))
    key -> value
  }

  override def close(): Unit = ()
}

其中toTimestamp只是一个函数,该函数返回从记录值中提取的时间戳。一旦执行,就会有一个NPE:

Exception in thread "...-6f3693b9-4e8d-4e65-9af6-928884320351-StreamThread-5" java.lang.NullPointerException
    at org.apache.kafka.streams.processor.internals.ProcessorContextImpl.forward(ProcessorContextImpl.java:110)
    at CustomTransformer.transform()
    at CustomTransformer.transform()
    at org.apache.kafka.streams.scala.kstream.KStream$$anon$1$$anon$2.transform(KStream.scala:302)
    at org.apache.kafka.streams.scala.kstream.KStream$$anon$1$$anon$2.transform(KStream.scala:300)
    at 

实际上发生的是ProcessorContextImpl失败:

public <K, V> void forward(final K key, final V value, final To to) {
    toInternal.update(to);
    if (toInternal.hasTimestamp()) {
        recordContext.setTimestamp(toInternal.timestamp());
    }
    final ProcessorNode previousNode = currentNode();

因为recordContext尚未初始化(只能由KafkaStreams在内部完成)。

这是一个后续问题Set timestamp in output with Kafka Streams 1

2 个答案:

答案 0 :(得分:0)

如果使用transformer,则需要确保在调用Transformer时创建一个新的TransformerSupplier#get()对象。 (请参阅https://docs.confluent.io/current/streams/faq.html#why-do-i-get-an-illegalstateexception-when-accessing-record-metadata

在最初的问题中,我认为这是与导致NPE的context变量有关,但现在我意识到这与Kafka Streams内部有关。

Scala API在2.0.0中有一个错误,该错误可能导致重复使用同一Transformer实例(https://issues.apache.org/jira/browse/KAFKA-7250)的情况。我认为您正在遇到此错误。稍微重写代码应该可以解决问题。请注意,Kafka 2.0.1和Kafka 2.1.0包含修复程序。

答案 1 :(得分:0)

@ matthias-j-sax如果在Java代码中重复使用处理器,则行为相同。

    Topology topology = new Topology();
    MyProcessor myProcessor = new MyProcessor();
    topology.addSource("source", "topic-1")
            .addProcessor(
                    "processor",
                    () -> {
                        return myProcessor;
                    },
                    "source"
            )
            .addSink("sink", "topic-2", "processor");
    KafkaStreams streams = new KafkaStreams(topology, config);
    streams.start();