web-dev-qa-db-fra.com

Comment obtenir la sortie du collecteur de streaming de la console dans Zeppelin?

Je ne parviens pas à utiliser l'évier console avec PySpark Structured Streaming lorsque je l'utilise depuis Zeppelin. En gros, je ne vois aucun résultat imprimé à l'écran ni dans aucun fichier journal que j'ai trouvé.

Ma question: Quelqu'un at-il un exemple concret d'utilisation de PySpark Structured Streaming avec un récepteur produisant une sortie visible dans Apache Zeppelin? Idéalement, il utiliserait également la source de socket, ce qui est facile à tester.

J'utilise:

  • Ubuntu 16.04
  • spark-2.2.0-bin-hadoop2.7
  • zeppelin-0.7.3-bin-all
  • Python3

J'ai basé mon code sur l'exemple structuré-network_wordcount.py }. Cela fonctionne lorsqu'il est exécuté depuis le shell PySpark (./bin/pyspark --master local[2]); Je vois des tableaux par lot.

%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)

Je m'attendrais à voir des impressions des résultats pour chaque lot, mais à la place, je ne vois que Starting..., puis False, la valeur de retour de query.awaitTermination(20).

Dans un terminal séparé, je suis en train d'entrer des données dans une session nc -lk 9999 netcat pendant l'exécution de la procédure ci-dessus.

5
m01

L'évier de console n'est pas un bon choix pour le flux de travail basé sur un ordinateur portable interactif. Même en Scala, où la sortie peut être capturée, un appel awaitTermination (ou équivalent) est requis dans le même paragraphe, ce qui bloque effectivement la note. 

%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

La chaîne awaitTermination pourrait être remplacée par un appel autonome dans le même paragraphe fonctionnerait également:

%spark

val query = df
  .writeStream
  ...
  .start()

query.awaitTermination()

Sans cela, Zeppelin n’a aucune raison d’attendre une sortie. PySpark ajoute simplement un autre problème: l’exécution indirecte. Pour cette raison, même le blocage de la requête ne vous aidera pas ici.

De plus, une sortie continue du flux peut entraîner des problèmes de rendu et de mémoire lors de l'exploration de la note (il est possible d'utiliser le système d'affichage Zeppelin via l'API InterpreterContext ou REST pour obtenir un comportement un peu plus judicieux, où la sortie est écrasé ou effacé périodiquement).

Un meilleur choix pour les tests avec Zeppelin est collecteur de mémoire . De cette façon, vous pouvez lancer une requête sans bloquer:

%pyspark

query = (windowedCounts
  .writeStream
  .outputMode("complete")
  .format("memory")
  .queryName("some_name")
  .start())

et interroger le résultat à la demande dans un autre paragraphe:

%pyspark

spark.table("some_name").show()

Il peut être couplé avec des flux réactifs ou une solution similaire pour fournir des mises à jour par intervalles.

Il est également possible d'utiliser StreamingQueryListener avec les rappels Py4j pour coupler les événements rx à onQueryProgress, bien que les écouteurs de requête ne soient pas pris en charge dans PySpark et nécessitent un peu de code pour coller des éléments. Interface 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 = {}
}

construisez un bocal en ajustant la définition de génération pour refléter la version souhaitée de Scala et Spark:

scalaVersion := "2.11.8"  

val sparkVersion = "2.2.0"

libraryDependencies ++= Seq(
  "org.Apache.spark" %% "spark-sql" % sparkVersion,
  "org.Apache.spark" %% "spark-streaming" % sparkVersion
)

placez-le sur le chemin de classe Spark, 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

démarrer le serveur de rappel:

%pyspark

sc._gateway.start_callback_server()

et ajouter un auditeur:

%pyspark

from rx.subjects import Subject

class StreamingObserver(Subject):
    class Java:
        implements = ["com.example.spark.observer.PythonObserver"]

observer = StreamingObserver()
spark.streams.addListener(observer)

Enfin, vous pouvez utiliser subscribe et bloquer l'exécution:

%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 

La dernière étape doit être exécutée après le démarrage de la requête en continu.

Il est également possible de sauter rx et d'utiliser un observateur minimal comme ceci:

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

Cela donne un peu moins de contrôle que Subject (un inconvénient est que cela peut interférer avec l’impression de code sur stdout et ne peut être arrêté que par en supprimant l’auditeur . Avec Subject, vous pouvez facilement disposesubscribed observateur, une fois que vous avez terminé ), mais devrait fonctionner plus ou moins de la même manière. 

Notez que toute action de blocage sera suffisante pour capturer la sortie du programme d'écoute et qu'elle ne doit pas nécessairement être exécutée dans la même cellule. Par exemple

%pyspark

observer = StreamingObserver()
spark.streams.addListener(observer)

et

%pyspark

import time

time.sleep(42)

fonctionnerait de la même manière, en imprimant la table pour un intervalle de temps défini.

Pour être complet, vous pouvez implémenter StreamingQueryManager.removeListener

8
user6910411

zeppelin-0.7.3-bin-all utilise Spark 2.1.0 (donc pas de format rate pour tester le streaming structuré avec, malheureusement).

 enter image description here


Assurez-vous que lorsque vous start, une requête en flux continu avec socket source nc -lk 9999 a déjà été démarrée (sinon, la requête s'arrête tout simplement).

Assurez-vous également que la requête est bien opérationnelle.

val lines = spark
  .readStream
  .format("socket")
  .option("Host", "localhost")
  .option("port", 9999)
  .load
val q = lines.writeStream.format("console").start

 enter image description here

Il est en effet vrai que vous ne pourrez pas voir la sortie dans un cahier Zeppelin éventuellement car:

  1. Les requêtes en streaming démarrent sur leurs propres threads (ce qui semble hors de portée de Zeppelin)

  2. console sink écrit sur la sortie standard (utilise l'opérateur Dataset.show sur ce thread séparé).

Tout cela rend "intercepter" la sortie non disponible dans Zeppelin.

Nous arrivons donc à répondre à la vraie question:

Où est la sortie standard écrite dans Zeppelin?

Eh bien, avec une compréhension très limitée des composants internes de Zeppelin, j'ai pensé que cela pourrait être logs/zeppelin-interpreter-spark-[hostname].log, mais malheureusement, je n'ai pas pu trouver la sortie de l'évier console. C'est là que vous pouvez trouver les journaux de Spark (et du flux structuré en particulier) qui utilisent log4j mais que console sink n'utilise pas.

Il semble que votre seule solution à long terme consiste à écrire votre propre évier personnalisé semblable à console- et à utiliser un enregistreur log4j. Honnêtement, ce n'est pas si difficile que cela puisse paraître. Suivez les sources de la console évier .

0
Jacek Laskowski