使用Kafka Streams测试窗口聚合

时间:2018-10-04 09:28:23

标签: apache-kafka apache-kafka-streams

我正在使用Kafka Streams的TopologyTestDriver,以便对我们的数据管道进行测试。

它与我们所有简单的拓扑(包括使用Stores的有状态拓扑)一样具有魅力。 我的问题是,当我尝试使用此测试驱动程序以测试使用窗口聚合的拓扑时。

我已经复制了一个简单的示例,该示例求和了在10秒的窗口内用同一键接收到的整数。

This <b>is editable</b> this not <b>This is editable</b> again

}

我希望在这个测试用例中,除非我将挂钟时间提前10秒,否则什么都不会返回到输出主题...但是我得到以下输出

public class TopologyWindowTests {

TopologyTestDriver testDriver;
String INPUT_TOPIC = "INPUT.TOPIC";
String OUTPUT_TOPIC = "OUTPUT.TOPIC";

@Before
public void setup(){
    Properties config = new Properties();
    config.put(StreamsConfig.APPLICATION_ID_CONFIG, "test");
    config.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "dummy:1234");
    // EventProcessor is a <String,String> processor
    // so we set those serders
    config.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());
    config.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.Integer().getClass());
    testDriver = new TopologyTestDriver(defineTopology(),config,0L);
}

/**
 * topology test
 */
@Test
public void testTopologyNoCorrelation() throws IOException {
    ConsumerRecordFactory<String, Integer> factory = new ConsumerRecordFactory<>(INPUT_TOPIC, new StringSerializer(), new IntegerSerializer());
    testDriver.pipeInput(factory.create(INPUT_TOPIC,"k",2,1L));

    ProducerRecord<String, Integer> outputRecord = testDriver.readOutput(OUTPUT_TOPIC, new StringDeserializer(), new IntegerDeserializer());

    Assert.assertNull(outputRecord);
}

@After
public void tearDown() {
    testDriver.close();
}

/**
 * Defines topology
 * @return
 */
public Topology defineTopology(){
    StreamsBuilder builder = new StreamsBuilder();
    KStream<String,Integer> inputStream = builder.stream(INPUT_TOPIC);

    KTable<Windowed<String>, Integer> groupedMetrics = inputStream.groupBy((key,value)->key,
            Serialized.with(Serdes.String(),Serdes.Integer())).windowedBy(TimeWindows.of(TimeUnit.SECONDS.toMillis(10))).aggregate(
            ()-> 0,
            (String aggKey, Integer newValue, Integer aggValue)->{
                Integer val = aggValue+newValue;
                return val;
            },
            Materialized.<String,Integer,WindowStore<Bytes,byte[]>>as("GROUPING.WINDOW").withKeySerde(Serdes.String()).withValueSerde(Serdes.Integer())
    );

    groupedMetrics.toStream().map((key,value)->KeyValue.pair(key.key(),value)).to(OUTPUT_TOPIC);

    return builder.build();

}

我在这里错过了什么吗? 我正在使用kafka 2.0.0

更新

预先感谢

根据Matthias的回应,我准备了以下测试:

java.lang.AssertionError: expected null, but was:<ProducerRecord(topic=OUTPUT.TOPIC, partition=null, headers=RecordHeaders(headers = [], isReadOnly = false), key=k, value=2, timestamp=0)>

两个输入消息都已经发送了相同的时间戳,所以我期望输出主题中只有一个事件加上我的值之和。但是,我在输出中收到2个事件(第一个事件的值为2,第二个事件的值为4),我认为这不是拓扑的理想行为。

1 个答案:

答案 0 :(得分:1)

默认情况下,Kafka Streams在事件时间上进行窗口操作,而不是 wall-clock-time 。这保证了确定性处理的语义(壁钟时间处理固有地是不确定性的)。查看文档以获取更多详细信息:https://docs.confluent.io/current/streams/concepts.html#time

因此,输入记录的时间戳确定记录放置在哪个窗口中。另外,输入记录的时间戳会基于这些事件时间戳提前内部跟踪的“流时间”。

还请注意,Kafka Streams遵循连续处理模型,并且会发出 updated 而不是等待窗口结束条件。这对于处理迟到(即乱序数据)很重要。比较How to send final kafka-streams aggregation result of a time windowed KTable?https://www.confluent.io/blog/watermarks-tables-event-time-dataflow-model/

更新

这是因为“更新”处理模型。汇总时,每个输入记录都会更新“当前”结果,并生成一个“当前结果输出记录”。对于每个记录(不是每个时间戳)都会发生这种情况。