web-dev-qa-db-ja.com

spark構造化ストリーミングで個別のストリーミングクエリを実行する

2つの異なるウィンドウでストリームを集約し、コンソールに出力しようとしています。ただし、最初のストリーミングクエリのみが出力されます。 tenSecsQはコンソールに出力されません。

SparkSession spark = SparkSession
    .builder()
    .appName("JavaStructuredNetworkWordCountWindowed")
    .config("spark.master", "local[*]")
    .getOrCreate();

Dataset<Row> lines = spark
    .readStream()
    .format("socket")
    .option("Host", Host)
    .option("port", port)
    .option("includeTimestamp", true)
    .load();

Dataset<Row> words = lines
    .as(Encoders.Tuple(Encoders.STRING(), Encoders.TIMESTAMP()))
    .toDF("Word", "timestamp");

// 5 second window
Dataset<Row> fiveSecs = words
    .groupBy(
         functions.window(words.col("timestamp"), "5 seconds"),
         words.col("Word")
    ).count().orderBy("window");

// 10 second window
Dataset<Row> tenSecs = words
    .groupBy(
          functions.window(words.col("timestamp"), "10 seconds"),
          words.col("Word")
    ).count().orderBy("window");

5秒と10秒の両方の集約ストリームに対してストリーミングクエリをトリガーします。 10秒ストリームの出力は出力されません。 5sのみがコンソールに出力されます

// Start writeStream() for 5s window
StreamingQuery fiveSecQ = fiveSecs.writeStream()
    .queryName("5_secs")
    .outputMode("complete")
    .format("console")
    .option("truncate", "false")
    .start();

// Start writeStream() for 10s window
StreamingQuery tenSecsQ = tenSecs.writeStream()
    .queryName("10_secs")
    .outputMode("complete")
    .format("console")
    .option("truncate", "false")
    .start();

tenSecsQ.awaitTermination();
14
atom

私はこの質問を調査してきました。

概要:構造化ストリーミングの各クエリは、sourceデータを消費します。ソケットソースは、定義されたクエリごとに新しい接続を作成します。この場合に見られる動作は、ncが最初の接続にのみ入力データを配信しているためです。

今後、接続されたソケットソースが開いている各接続に同じデータを配信することを確認できない限り、ソケット接続を介して複数の集計を定義することはできません。


この質問については、Sparkメーリングリストで話し合いました。Databricksの開発者であるShixiongZhuが答えました。

Sparkは、クエリごとに1つの接続を作成します。観察した動作は、「nc-lk」がどのように機能するかによるものです。 netstatを使用してtcp接続を確認すると、2つのクエリを開始したときに2つの接続があることがわかります。ただし、「nc」は入力を1つの接続にのみ転送します。

小さな実験を定義して、この動作を検証しました。まず、開いている各接続にランダムな単語を配信する SimpleTCPWordServer と、2つのクエリを宣言する基本的な構造化ストリーミングジョブを作成しました。それらの唯一の違いは、2番目のクエリがその出力を区別するために追加の定数列を定義することです。

val lines = spark
    .readStream
    .format("socket")
    .option("Host", "localhost")
    .option("port", "9999")
    .option("includeTimestamp", true)
    .load()

val q1 = lines.writeStream
  .outputMode("append")
  .format("console")
  .trigger(Trigger.ProcessingTime("5 seconds"))
  .start()

val q2 = lines.withColumn("foo", lit("foo")).writeStream
  .outputMode("append")
  .format("console")
  .trigger(Trigger.ProcessingTime("7 seconds"))
  .start()

StructuredStreamingが1つのストリームのみを消費する場合、両方のクエリによって同じ単語が配信されるはずです。各クエリが個別のストリームを消費する場合、各クエリによって異なる単語が報告されます。

これは観察された出力です:

-------------------------------------------
Batch: 0
-------------------------------------------
+--------+-------------------+
|   value|          timestamp|
+--------+-------------------+
|champion|2017-08-14 13:54:51|
+--------+-------------------+

+------+-------------------+---+
| value|          timestamp|foo|
+------+-------------------+---+
|belong|2017-08-14 13:54:51|foo|
+------+-------------------+---+

-------------------------------------------
Batch: 1
-------------------------------------------
+-------+-------------------+---+
|  value|          timestamp|foo|
+-------+-------------------+---+
| agenda|2017-08-14 13:54:52|foo|
|ceiling|2017-08-14 13:54:52|foo|
|   bear|2017-08-14 13:54:53|foo|
+-------+-------------------+---+

-------------------------------------------
Batch: 1
-------------------------------------------
+----------+-------------------+
|     value|          timestamp|
+----------+-------------------+
|    breath|2017-08-14 13:54:52|
|anticipate|2017-08-14 13:54:52|
|   amazing|2017-08-14 13:54:52|
|    bottle|2017-08-14 13:54:53|
| calculate|2017-08-14 13:54:53|
|     asset|2017-08-14 13:54:54|
|      cell|2017-08-14 13:54:54|
+----------+-------------------+

各クエリのストリームが異なることがはっきりとわかります。 TCPバックエンドサーバーが開いている各接続にまったく同じデータを配信することを保証できない限り、socket sourceによって配信されるデータに対して複数の集計を定義することはできないように思われます。

21
maasg