web-dev-qa-db-ja.com

理解kafkaストリームグループByとウィンドウ

GroupBy/groupByIdとkafkaストリーミングのウィンドウ処理の概念を理解できません。私の目標は、一定期間(5秒など)のストリームデータを集約することです。ストリーミングデータは次のようになります。 :

{"value":0,"time":1533875665509}
{"value":10,"time":1533875667511}
{"value":8,"time":1533875669512}

時間はミリ秒単位(Epoch)です。ここで、私のタイムスタンプは私のメッセージにあり、キーにはありません。そして、5秒ウィンドウの値を平均したいと思います。

これが私が試しているコードですが、動作させることができないようです

builder.<String, String>stream("my_topic")
   .map((key, val) -> { TimeVal tv = TimeVal.fromJson(val); return new KeyValue<Long, Double>(tv.time, tv.value);})
   .groupByKey(Serialized.with(Serdes.Long(), Serdes.Double()))
   .windowedBy(TimeWindows.of(5000))
   .count()
   .toStream()
   .foreach((key, val) -> System.out.println(key + " " + val));

トピックが2秒ごとにメッセージを生成している場合でも、このコードは何も出力しません。 Ctrl + Cを押すと、次のように出力されます。

[1533877059029@1533877055000/1533877060000] 1
[1533877061031@1533877060000/1533877065000] 1
[1533877063034@1533877060000/1533877065000] 1
[1533877065035@1533877065000/1533877070000] 1
[1533877067039@1533877065000/1533877070000] 1

この出力は私には意味がありません。

関連コード:

public class MessageTimeExtractor implements TimestampExtractor {
    @Override
    public long extract(ConsumerRecord<Object, Object> record,  long previousTimestamp) {
        String str = (String)record.value();
        TimeVal tv = TimeVal.fromJson(str);
        return tv.time;
    }
}

public class TimeVal
{
    final public long time;
    final public double value;
    public TimeVal(long tm, double val) {
        this.time = tm;
        this.value = val;
    }
   public static TimeVal fromJson(String val) {
       Gson gson = new GsonBuilder().create();
       TimeVal tv = gson.fromJson(val, TimeVal.class);
       return tv;
   }
}

質問:

グループ化するためにシリアライザー/デシリアライザーを渡す必要があるのはなぜですか。オーバーロードの中にはValueStoreも使用するものがありますが、それは何ですか?グループ化すると、グループ化されたストリームでデータがどのように表示されますか?

ウィンドウストリームはグループストリームとどのように関連していますか?

上記では、ストリーミング方式で印刷することを期待していました。つまり、5秒ごとにバッファリングしてから、カウントしてから印刷します。コマンドプロンプトでCtrl + cを押すと1回だけ印刷されます。つまり、印刷してから終了します

4
x64

入力データにキーがないようです(これが間違っている場合は修正してください)。さらに、グローバル集計を実行したいようです。

一般に、グループ化は、ストリームをサブストリームに分割するためのものです。これらのサブストリームは、キーごとに構築されます(つまり、キーごとに1つの論理サブストリーム)。コードスニペットのキーとしてタイムスタンプを設定し、タイムスタンプごとにサブストリームを生成します。これは意図されたものではないと思います。

グローバル集計を行う場合は、すべてのレコードを単一のサブストリームにマップする必要があります。つまり、groupBy()のすべてのレコードに同じキーを割り当てる必要があります。集計は単一のスレッドで計算する必要があるため、グローバル集計はスケーリングされないことに注意してください。したがって、これは小さなワークロードでのみ機能します。

ウィンドウは、生成された各サブストリームに適用されてウィンドウが構築され、ウィンドウごとに集計が計算されます。ウィンドウは、Timestampエクストラクタによって返されるタイムスタンプに基づいて構築されます。この目的のために値のタイムスタンプをすでに抽出する実装があるようです。

トピックが2秒ごとにメッセージを生成している場合でも、このコードは何も出力しません。 Ctrl + Cを押すと、次のように出力されます。

デフォルトでは、Kafka Streamsは内部キャッシュを使用し、キャッシュはコミット時にフラッシュされます。これはデフォルトで30秒ごとに、またはアプリケーションを停止したときに発生します。キャッシュを無効にする必要があります。以前の結果を参照してください(cf. https://docs.confluent.io/current/streams/developer-guide/memory-mgmt.html

グループ化するためにシリアライザー/デシリアライザーを渡す必要があるのはなぜですか。

データを再配布する必要があり、これはKafkaのトピックを介して行われるためです。 Kafka Streamsは分散セットアップ用に構築されており、同じアプリケーションの複数のインスタンスが並行して実行され、水平方向にスケールアウトすることに注意してください。

ところで、このブログ投稿では、Kafka Streams: https://www.confluent.io/blog/watermarks-tables-event-)の実行モデルについても興味深いかもしれません。時間-データフロー-モデル/

6
Matthias J. Sax

ウィンドウDSLの性質を誤解しているようです。

これは、kafkaプラットフォームによって処理される内部メッセージのタイムスタンプに対して機能し、時間情報をエンコードする特定のメッセージタイプの任意のプロパティに対しては機能しません。また、このウィンドウは間隔にグループ化されません。これはスライディングウィンドウです。これは、取得する集計が現在のメッセージの前の最後の5秒間であることを意味します。

また、すべてのグループ要素を同じグループに結合するには、同じキーが必要です(例:null)。あなたの例では、keyは一種のエントリ固有のタイムスタンプであるため、グループ内の要素は1つだけになります。

0
Ivan Klass