Zeppelinから実行すると、console
シンクが PySpark Structured Streaming で動作するようになるのに苦労しています。基本的に、画面に出力された結果や、見つけたログファイルには出力されません。
私の質問:Apache Zeppelinで表示される出力を生成するシンクでPySpark構造化ストリーミングを使用する実用的な例はありますか?テストが簡単なため、理想的にはソケットソースも使用します。
私が使用しています:
structured_network_wordcount.pyの例 に基づいてコードを作成しました。 PySparkシェル(_./bin/pyspark --master local[2]
_);から実行すると機能します。バッチごとのテーブルが表示されます。
_%pyspark
# structured streaming
from pyspark.sql.functions import *
lines = spark\
.readStream\
.format('socket')\
.option('Host', 'localhost')\
.option('port', 9999)\
.option('includeTimestamp', 'true')\
.load()
# Split the lines into words, retaining timestamps
# split() splits each line into an array, and explode() turns the array into multiple rows
words = lines.select(
explode(split(lines.value, ' ')).alias('Word'),
lines.timestamp
)
# Group the data by window and Word and compute the count of each group
windowedCounts = words.groupBy(
window(words.timestamp, '10 seconds', '1 seconds'),
words.Word
).count().orderBy('window')
# Start running the query that prints the windowed Word counts to the console
query = windowedCounts\
.writeStream\
.outputMode('complete')\
.format('console')\
.option('truncate', 'false')\
.start()
print("Starting...")
query.awaitTermination(20)
_
各バッチの結果のプリントアウトが表示されると思いますが、代わりに_Starting...
_が表示され、次にFalse
、query.awaitTermination(20)
の戻り値が表示されます。
別の端末で、上記の実行中に_nc -lk 9999
_ netcatセッションにデータを入力しています。
コンソールシンクは、インタラクティブなノートブックベースのワークフローには適していません。出力をキャプチャできるScalaの場合でも、同じ段落にawaitTermination
呼び出し(または同等のもの)が必要であり、メモを効果的にブロックします。
%spark
spark
.readStream
.format("socket")
.option("Host", "localhost")
.option("port", "9999")
.option("includeTimestamp", "true")
.load()
.writeStream
.outputMode("append")
.format("console")
.option("truncate", "false")
.start()
.awaitTermination() // Block execution, to force Zeppelin to capture the output
チェーンされたawaitTermination
は、同じ段落内のスタンドアロン呼び出しで置き換えることができますも機能します。
%spark
val query = df
.writeStream
...
.start()
query.awaitTermination()
それがなければ、ツェッペリンは出力を待つ理由がありません。 PySparkは、それに加えて、間接実行という別の問題を追加するだけです。そのため、クエリをブロックしても、ここでは役に立ちません。
さらに、ストリームからの連続出力は、ノートを参照するときにレンダリングの問題とメモリの問題を引き起こす可能性があります(InterpreterContext
またはREST APIを介してZeppelin表示システムを使用して、出力が上書きされるか、定期的にクリアされる、もう少し賢明な動作)。
Zeppelinでテストするためのより良い選択は memory sink です。これにより、ブロックせずにクエリを開始できます。
%pyspark
query = (windowedCounts
.writeStream
.outputMode("complete")
.format("memory")
.queryName("some_name")
.start())
別の段落でオンデマンドで結果をクエリします。
%pyspark
spark.table("some_name").show()
リアクティブストリーム または同様のソリューションと組み合わせて、間隔ベースの更新を提供できます。
Py4parkコールバックでStreamingQueryListener
を使用してrx
をonQueryProgress
イベントと結合することもできますが、クエリリスナーはPySparkではサポートされていないため、少しのコードが必要です物事を一緒に。 Scalaインターフェース:
package com.example.spark.observer
import org.Apache.spark.sql.streaming.StreamingQueryListener
import org.Apache.spark.sql.streaming.StreamingQueryListener._
trait PythonObserver {
def on_next(o: Object): Unit
}
class PythonStreamingQueryListener(observer: PythonObserver)
extends StreamingQueryListener {
override def onQueryProgress(event: QueryProgressEvent): Unit = {
observer.on_next(event)
}
override def onQueryStarted(event: QueryStartedEvent): Unit = {}
override def onQueryTerminated(event: QueryTerminatedEvent): Unit = {}
}
jarをビルドし、必要なScalaおよびSparkバージョンを反映するようにビルド定義を調整します。
scalaVersion := "2.11.8"
val sparkVersion = "2.2.0"
libraryDependencies ++= Seq(
"org.Apache.spark" %% "spark-sql" % sparkVersion,
"org.Apache.spark" %% "spark-streaming" % sparkVersion
)
Spark classpath、patch StreamingQueryManager
に置きます:
%pyspark
from pyspark.sql.streaming import StreamingQueryManager
from pyspark import SparkContext
def addListener(self, listener):
jvm = SparkContext._active_spark_context._jvm
jlistener = jvm.com.example.spark.observer.PythonStreamingQueryListener(
listener
)
self._jsqm.addListener(jlistener)
return jlistener
StreamingQueryManager.addListener = addListener
コールバックサーバーを起動します。
%pyspark
sc._gateway.start_callback_server()
そしてリスナーを追加します:
%pyspark
from rx.subjects import Subject
class StreamingObserver(Subject):
class Java:
implements = ["com.example.spark.observer.PythonObserver"]
observer = StreamingObserver()
spark.streams.addListener(observer)
最後に、subscribe
を使用して実行をブロックできます。
%pyspark
(observer
.map(lambda p: p.progress().name())
# .filter() can be used to print only for a specific query
.subscribe(lambda n: spark.table(n).show() if n else None))
input() # Block execution to capture the output
最後のステップは、クエリのストリーミングを開始した後に実行する必要があります。
rx
をスキップして、次のような最小限のオブザーバーを使用することもできます。
class StreamingObserver(object):
class Java:
implements = ["com.example.spark.observer.PythonObserver"]
def on_next(self, value):
try:
name = value.progress().name()
if name:
spark.table(name).show()
except: pass
Subject
よりも少し制御が少なくなります(1つの注意点は、これが他のコードのstdoutへの出力を妨げることがあり、 リスナーの削除 によってのみ停止できることです。Subject
dispose
subscribed
オブザーバーは簡単に実行できますが、それ以外はほぼ同じように機能します。
リスナーからの出力をキャプチャするには、ブロッキングアクションがあれば十分であり、同じセルで実行する必要がないことに注意してください。例えば
%pyspark
observer = StreamingObserver()
spark.streams.addListener(observer)
そして
%pyspark
import time
time.sleep(42)
同様の方法で、定義された時間間隔でテーブルを印刷します。
完全を期すために、StreamingQueryManager.removeListener
。
zeppelin-0.7.3-bin-all
はSpark 2.1.0を使用します(残念ながら、構造化ストリーミングをテストするrate
形式はありません)。
start
_socket
source nc -lk 9999
を使用したストリーミングクエリが既に開始されていることを確認してください(クエリは単に停止するため)。
また、クエリが実際に実行されていることを確認してください。
val lines = spark
.readStream
.format("socket")
.option("Host", "localhost")
.option("port", 9999)
.load
val q = lines.writeStream.format("console").start
次の理由により、Zeppelinノートブックで出力を見ることができないことは確かに事実です。
ストリーミングクエリは独自のスレッドで開始されます(これはZeppelinの範囲外のようです)
console
シンク 標準出力に書き込み (別のスレッドでDataset.show
演算子を使用)。
これにより、ツェッペリンでは出力を「傍受」できなくなります。
だから私たちは本当の質問に答えるようになります:
ツェッペリン語の標準出力はどこに書き込まれますか?
まあ、ツェッペリンの内部についての理解は非常に限られているので、それはlogs/zeppelin-interpreter-spark-[hostname].log
かもしれないと思いましたが、残念ながらconsole
シンクからの出力を見つけることができませんでした。ここで、log = 4を使用し、console
シンクは使用しないSpark(および構造化ストリーミング)からのログを見つけることができます。
長期的な解決策は、独自のconsole
のようなカスタムシンクを作成し、log4jロガーを使用することでした。正直なところ、それは思ったほど難しくはありません。 コンソールシンクのソース に従います。