J'utilise Spark structured streaming
pour traiter les enregistrements lus dans Kafka
. Voici ce que j'essaie de réaliser:
(a) Chaque enregistrement est un Tuple2
de type (Timestamp, DeviceId)
.
(b) J'ai créé un Dataset[DeviceId]
statique qui contient l'ensemble de tous les ID de périphérique valides (de type DeviceId
) qui devraient être vus dans le flux Kafka
.
(c) Je dois écrire une requête Spark structured streaming
qui
(i) Groups records by their timestamp into 5-minute windows
(ii) For each window, get the list of valid device IDs that were **not** seen in that window
Par exemple, supposons que la liste de tous les ID de périphérique valides soit [A,B,C,D,E]
et que les enregistrements kafka dans une fenêtre donnée de 5 minutes contiennent les ID de périphérique [A,B,E]
. Ensuite, pour cette fenêtre, la liste des ID de périphérique non vus que je recherche est [C,D]
.
Question
except()
et join()
que Dataset
expose. Cependant, ils ont tous deux émis une exception d'exécution se plaignant qu'aucune de ces opérations n'est prise en charge sur un streaming Dataset
.Voici un extrait de mon code:
val validDeviceIds: Dataset[(DeviceId, Long)] = spark.createDataset[DeviceId](listOfAllDeviceIds.map(id => (id, 0L)))
case class KafkaRecord(timestamp: TimestampType, deviceId: DeviceId)
// kafkaRecs is the data stream from Kafka - type is Dataset[KafkaRecord]
val deviceIdsSeen = kafkaRecs
.withWatermark("timestamp", "5 minutes")
.groupBy(window($"timestamp", "5 minutes", "5 minutes"), $"deviceId")
.count()
.map(row => (row.getLong(0), 1L))
.as[(Long, Long)]
val unseenIds = deviceIdsSeen.join(validDeviceIds, Seq("_1"), "right_outer")
.filter(row => row.isNullAt(1))
.map(row => row.getLong(0))
La dernière instruction lève l'exception suivante:
Caused by: org.Apache.spark.sql.AnalysisException: Right outer join with a streaming DataFrame/Dataset on the left is not supported;;
Merci d'avance.
La situation avec join operations
dans la diffusion structurée par étincelle ressemble à ceci: la transmission DataFrames
peut être jointe à static DataFrames
afin de créer ultérieurement un nouveau streaming DataFrames
. Mais outer joins
entre une streaming
et un static Datasets
sont pris en charge conditionnellement, alors que right/left joins
avec un streaming Dataset
ne sont pas pris en charge en général par le flux structuré. En conséquence, vous avez été confronté à AnalysisException , qui a été émise pendant que vous tentiez de créer une jointure de données statiques avec des données en continu. Comme preuve de mes paroles, vous pouvez regarder le code source de l'étincelle. Cette exception ligne renvoie ce qui indique que l'opération que vous avez essayée n'est pas prise en charge.
J'ai essayé de faire une opération de jointure sur stream of DataFrames
avec une DataFrames
statique.
val streamingDf = sparkSession
.readStream
.format("kafka")
.option("kafka.bootstrap.servers", "127.0.0.1:9092")
.option("subscribe", "structured_topic")
.load()
val lines = spark.readStream
.format("socket")
.option("Host", "localhost")
.option("port", 9999)
.load()
val staticDf = Seq((1507831462 , 100)).toDF("Timestamp", "DeviceId")
//Inner Join
streamingDf.join(staticDf, "Timestamp")
line.join(staticDf, "Timestamp")
//Left Join
streamingDf.join(staticDf, "Timestamp", "left_join")
line.join(staticDf, "Timestamp", "left_join")
Comme vous le voyez, en plus de consommer des données de Kafka
, je lis les données du socket lancé via nc
(netcat), cela simplifie considérablement la vie lorsque vous testez une application de flux . Cette approche me convient très bien avec Kafka
et socket
comme source de données.
J'espère que cette aide.
Les jointures externes avec le jeu de données en continu du côté opposé ne sont tout simplement pas prises en charge :
- Les jointures externes entre une diffusion en continu et une statique Les jeux de données sont conditionnellement pris en charge .
- La jointure externe complète avec un jeu de données en continu n'est pas prise en charge.
- La jointure externe gauche avec un jeu de données en continu à droite n'est pas prise en charge.
- La jointure externe droite avec un jeu de données en continu sur la gauche n'est pas prise en charge
Si l'autre Dataset
est petit, vous pouvez utiliser Map
ou une structure similaire, broadcast
, et le référencer à l'intérieur de UserDefinedFunction
.
val map: Broadcast[Map[T, U]] = ???
val lookup = udf((x: T) => map.value.get(x))
df.withColumn("foo", lookup($"_1"))