簡體   English   中英

如何發送時間窗口 KTable 的最終 kafka-streams 聚合結果?

[英]How to send final kafka-streams aggregation result of a time windowed KTable?

我想做的是:

  1. 使用數字主題(Long's)中的記錄
  2. 聚合(計數)每 5 秒窗口的值
  3. 將 FINAL 聚合結果發送到另一個主題

我的代碼如下所示:

KStream<String, Long> longs = builder.stream(
            Serdes.String(), Serdes.Long(), "longs");

// In one ktable, count by key, on a five second tumbling window.
KTable<Windowed<String>, Long> longCounts = 
            longs.countByKey(TimeWindows.of("longCounts", 5000L));

// Finally, sink to the long-avgs topic.
longCounts.toStream((wk, v) -> wk.key())
          .to("long-counts");

看起來一切都按預期進行,但聚合被發送到每個傳入記錄的目標主題。 我的問題是如何只發送每個窗口的最終聚合結果?

在 Kafka Streams 中沒有“最終聚合”這樣的東西。 窗口始終保持打開狀態,以處理在窗口結束時間過后到達的亂序記錄。 然而,窗戶不會永遠保留。 一旦保留時間到期,它們就會被丟棄。 沒有關於何時丟棄窗口的特殊操作。

有關更多詳細信息,請參閱 Confluent 文檔: http : //docs.confluent.io/current/streams/

因此,對於聚合的每次更新,都會生成一條結果記錄(因為 Kafka Streams 也會更新亂序記錄的聚合結果)。 您的“最終結果”將是最新的結果記錄(在窗口被丟棄之前)。 根據您的用例,手動重復數據刪除將是解決問題的一種方法(使用較低級別的 API、 transform()process()

這篇博文也可能有所幫助: https : //timothyrenner.github.io/engineering/2016/08/11/kafka-streams-not-looking-at-facebook.html

另一篇不使用標點符號解決此問題的博客文章: http : //blog.inovatrend.com/2018/03/making-of-message-gateway-with-kafka.html

更新

使用KIP-328 ,添加了KTable#suppress()運算符,這將允許以嚴格的方式抑制連續更新並為每個窗口發出單個結果記錄; 權衡是增加延遲。

從 Kafka Streams 2.1 版開始,您可以使用suppress來實現這一點。

提到的 apache Kafka Streams 文檔中有一個示例,當用戶在一小時內發生的事件少於三個時,它會發送警報:

KGroupedStream<UserId, Event> grouped = ...;
grouped
  .windowedBy(TimeWindows.of(Duration.ofHours(1)).grace(ofMinutes(10)))
  .count()
  .suppress(Suppressed.untilWindowCloses(unbounded()))
  .filter((windowedUserId, count) -> count < 3)
  .toStream()
  .foreach((windowedUserId, count) -> sendAlert(windowedUserId.window(), windowedUserId.key(), count));

如此答案的更新中所述,您應該了解權衡。 此外,請注意suppress() 基於事件時間。

我遇到了這個問題,但我解決了這個問題,在固定窗口之后添加了 grace(0) 並使用了 Suppressed API

public void process(KStream<SensorKeyDTO, SensorDataDTO> stream) {

        buildAggregateMetricsBySensor(stream)
                .to(outputTopic, Produced.with(String(), new SensorAggregateMetricsSerde()));

    }

private KStream<String, SensorAggregateMetricsDTO> buildAggregateMetricsBySensor(KStream<SensorKeyDTO, SensorDataDTO> stream) {
        return stream
                .map((key, val) -> new KeyValue<>(val.getId(), val))
                .groupByKey(Grouped.with(String(), new SensorDataSerde()))
                .windowedBy(TimeWindows.of(Duration.ofMinutes(WINDOW_SIZE_IN_MINUTES)).grace(Duration.ofMillis(0)))
                .aggregate(SensorAggregateMetricsDTO::new,
                        (String k, SensorDataDTO v, SensorAggregateMetricsDTO va) -> aggregateData(v, va),
                        buildWindowPersistentStore())
                .suppress(Suppressed.untilWindowCloses(unbounded()))
                .toStream()
                .map((key, value) -> KeyValue.pair(key.key(), value));
    }


    private Materialized<String, SensorAggregateMetricsDTO, WindowStore<Bytes, byte[]>> buildWindowPersistentStore() {
        return Materialized
                .<String, SensorAggregateMetricsDTO, WindowStore<Bytes, byte[]>>as(WINDOW_STORE_NAME)
                .withKeySerde(String())
                .withValueSerde(new SensorAggregateMetricsSerde());
    }

在這里你可以看到結果

在此處輸入圖片說明

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM