Kafka Streams:自定义TimestampExtractor用于聚合

时间:2018-01-24 17:04:32

标签: apache-kafka apache-kafka-streams

我正在构建一个非常简单的KafkaStreams演示应用程序来测试用例。

我无法升级我正在使用的Kafka代理(当前版本为0.10.0),并且有一些由0.10.0之前的生产者编写的消息,因此我使用的是自定义TimestampExtractor,我在主类的开头添加了默认配置:

config.put(StreamsConfig.DEFAULT_TIMESTAMP_EXTRACTOR_CLASS_CONFIG, GenericRecordTimestampExtractor.class);

从我的源主题消费时,这非常好用。但是当使用聚合运算符时,我遇到异常,因为在使用内部聚合主题时使用FailOnInvalidTimestamp TimestampExtractor实现而不是自定义实现

Streams应用程序的代码如下所示:

...

KStream<String, MyValueClass> clickStream = streamsBuilder
              .stream("mytopic", Consumed.with(Serdes.String(), valueClassSerde));

KTable<Windowed<Long>, Long> clicksByCustomerId = clickStream
              .map(((key, value) -> new KeyValue<>(value.getId(), value)))
              .groupByKey(Serialized.with(Serdes.Long(), valueClassSerde))
              .windowedBy(TimeWindows.of(TimeUnit.MINUTES.toMillis(1)))
              .count();
...

我遇到的例外情况如下:

    Exception in thread "click-aggregator-b9d77f2e-0263-4fa3-bec4-e48d4d6602ab-StreamThread-1" org.apache.kafka.streams.errors.StreamsException: 
Input record ConsumerRecord(topic = click-aggregator-KSTREAM-AGGREGATE-STATE-STORE-0000000002-repartition, partition = 9, offset = 0, CreateTime = -1, serialized key size = 8, serialized value size = 652, headers = RecordHeaders(headers = [], isReadOnly = false), key = 11230, value = org.example.MyValueClass@2a3f2ea2) has invalid (negative) timestamp. 
Possibly because a pre-0.10 producer client was used to write this record to Kafka without embedding a timestamp, or because the input topic was created before upgrading the Kafka cluster to 0.10+. Use a different TimestampExtractor to process this data.

现在的问题是:在从内部聚合主题中读取时,有什么方法可以让Kafka Streams使用自定义TimestampExtractor(最好还是使用Streams DSL时)?

3 个答案:

答案 0 :(得分:5)

您无法更改时间戳提取器(从v1.0.0开始)。出于正确原因,这是不允许的。

但我真的很想知道,首先如何将带有时间戳-1的记录写入此主题。 Kafka Streams使用写入记录时自定义提取程序提供的时间戳。另请注意,KafkaProducer不允许写入带有负时间戳的记录。

因此,我能想到的唯一解释是,其他一些制作人确实写入了重新分区主题 - 这是不允许的......只有Kafka Streams应该写入重新分配的主题。

我想,您需要删除此主题,让Kafka Streams重新创建它以恢复干净状态。

来自其他答案的讨论/评论:

  

您需要0.10+格式才能使用Kafka Streams。如果您升级经纪人并保持0.9格式或更早版本,Kafka Streams可能无法按预期工作。

答案 1 :(得分:0)

众所周知的问题:-)。我在项目中遇到了同样的问题,这些客户仍在使用像0.9这样的老Kafka客户端,并且在与某些&#34;未经认证的&#34; .NET客户端。

因此我写了专门的课程:

public class MyTimestampExtractor implements TimestampExtractor {

    private static final Logger LOG = LogManager.getLogger( MyTimestampExtractor.class );

    @Override
    public long extract ( ConsumerRecord<Object, Object> consumerRecord, long previousTimestamp ) {
        final long timestamp = consumerRecord.timestamp();

        if ( timestamp < 0 ) {
            final String msg = consumerRecord.toString().trim();
            LOG.warn( "Record has wrong Kafka timestamp: {}. It will be patched with local timestamp. Details: {}", timestamp, msg );
            return System.currentTimeMillis();
        }

        return timestamp;
    }
}

当有许多消息时,您可能会跳过记录,因为它可能会泛滥。

答案 2 :(得分:0)

在阅读Matthias的回答后,我仔细检查了一切,问题的原因是Kafka Broker和Kafka Streams应用程序之间的版本不兼容。 我愚蠢到使用Kafka Streams 1.0.0和0.10.1.1 Broker,这在Kafka Wiki here中明确表示不兼容。

编辑(thx to Matthias):问题的实际原因是我们的0.10.1.x代理使用的日志格式仍然是0.9.0.x,这与Kafka Streams不兼容。