Apache Beam:使用Withtimestamp分配事件时间时出错

时间:2019-01-16 11:30:31

标签: streaming apache-beam

我有一个无限的Kafka流,它使用以下字段发送数据

{"identifier": "xxx", "value": 10.0, "ts":"2019-01-16T10:51:26.326242+0000"}

我使用用于kafka的apache光束sdk读取流

import org.apache.beam.sdk.io.kafka.KafkaIO;
pipeline.apply(KafkaIO.<Long, String>read()
                    .withBootstrapServers("kafka:9092")
                    .withTopic("test")
                    .withKeyDeserializer(LongDeserializer.class)
                    .withValueDeserializer(StringDeserializer.class)
                    .updateConsumerProperties(ImmutableMap.of("enable.auto.commit", "true")) 
                    .updateConsumerProperties(ImmutableMap.of("group.id", "Consumer1"))
                    .commitOffsetsInFinalize()
                    .withoutMetadata()))

由于我想使用事件时间(在我的示例中为“ ts”)进行窗口显示,因此我解析了输入字符串,并将输入数据流的“ ts”字段分配为时间戳。

PCollection<Temperature> tempCollection = p.apply(new SetupKafka())
                    .apply(ParDo.of(new ReadFromTopic()))
                    .apply("ParseTemperature", ParDo.of(new ParseTemperature()));

tempCollection.apply("AssignTimeStamps", WithTimestamps.of(us -> new Instant(us.getTimestamp())));  

窗口函数和计算方法如下:

PCollection<Output> output = tempCollection.apply(Window
                .<Temperature>into(FixedWindows.of(Duration.standardSeconds(30)))
                .triggering(AfterWatermark.pastEndOfWindow()
                        .withLateFirings(AfterProcessingTime.pastFirstElementInPane().plusDelayOf(Duration.standardSeconds(10))))
                .withAllowedLateness(Duration.standardDays(1))
                .accumulatingFiredPanes())
                .apply(new ComputeMax());

我将数据流到输入流中的时间比当前utc时间晚5秒钟,因为在实际的场景事件中,时间戳通常早于处理时间戳。

我收到以下错误:

  

无法使用时间戳输出2019-01-16T11:15:45.560Z。输出量   时间戳记不得早于当前输入的时间戳记   (2019-01-16T11:16:50.640Z)减去允许的偏斜(0毫秒)。   有关更改的详细信息,请参见DoFn#getAllowedTimestampSkew()Javadoc   允许的偏斜。

如果我注释掉 AssignTimeStamps 的行,则没有错误,但我想这是在考虑处理时间。

如何确保我的计算和窗口基于事件时间而不是处理时间?

请提供一些有关如何处理这种情况的信息。

2 个答案:

答案 0 :(得分:0)

您是否有机会使用时间戳策略尝试此操作,对不起,我还没有亲自尝试过,但是我相信使用2.9.0时,您应该结合使用KafkaIO阅读来考虑使用该策略。

https://beam.apache.org/releases/javadoc/2.9.0/org/apache/beam/sdk/io/kafka/KafkaIO.Read.html#withTimestampPolicyFactory-org.apache.beam.sdk.io.kafka.TimestampPolicyFactory-

答案 1 :(得分:0)

要能够使用自定义时间戳,首先您需要通过扩展TimestampPolicy<KeyT,ValueT>

来实现CustomTimestampPolicy

例如:

public class CustomFieldTimePolicy extends TimestampPolicy<String, Foo> {


protected Instant currentWatermark;

public CustomFieldTimePolicy(Optional<Instant> previousWatermark) {
    currentWatermark = previousWatermark.orElse(BoundedWindow.TIMESTAMP_MIN_VALUE);
}


@Override
public Instant getTimestampForRecord(PartitionContext ctx, KafkaRecord<String, Foo> record) {
    currentWatermark = new Instant(record.getKV().getValue().getTimestamp());
    return currentWatermark;
}

@Override
public Instant getWatermark(PartitionContext ctx) {
    return currentWatermark;
}

}

然后,当您使用功能接口TimestampPolicyFactory设置KafkaIO源时,您需要传递自定义的TimestampPolicy。

KafkaIO.<String, Foo>read().withBootstrapServers("http://localhost:9092")
                .withTopic("foo")
                .withKeyDeserializer(StringDeserializer.class)
                .withValueDeserializerAndCoder(KafkaAvroDeserializer.class, AvroCoder.of(Foo.class)) //if you use avro
                .withTimestampPolicyFactory((tp, previousWatermark) -> new CustomFieldTimePolicy(previousWatermark))
                .updateConsumerProperties(kafkaProperties))

此行负责创建新的timestampPolicy,传递相关分区和先前的检查点水印,请参见documentation

withTimestampPolicyFactory(tp, previousWatermark) -> new CustomFieldTimePolicy(previousWatermark))