web-dev-qa-db-fra.com

Pourquoi l'application Spark échoue-t-elle avec "ClassNotFoundException: Impossible de trouver la source de données: kafka" en tant qu'uber-jar avec l'assembly sbt?

J'essaie d'exécuter un exemple du type https://github.com/Apache/spark/blob/master/examples/src/main/scala/org/Apache/spark/examples/sql/streaming/StructuredKafkaWordCount.scala. . J'ai commencé avec le guide de programmation Spark Structured Streaming à http://spark.Apache.org/docs/latest/structured-streaming-programming-guide.html

Mon code est

package io.boontadata.spark.job1

import org.Apache.spark.sql.SparkSession

object DirectKafkaAggregateEvents {
  val FIELD_MESSAGE_ID = 0
  val FIELD_DEVICE_ID = 1
  val FIELD_TIMESTAMP = 2
  val FIELD_CATEGORY = 3
  val FIELD_MEASURE1 = 4
  val FIELD_MEASURE2 = 5

  def main(args: Array[String]) {
    if (args.length < 3) {
      System.err.println(s"""
        |Usage: DirectKafkaAggregateEvents <brokers> <subscribeType> <topics>
        |  <brokers> is a list of one or more Kafka brokers
        |  <subscribeType> sample value: subscribe
        |  <topics> is a list of one or more kafka topics to consume from
        |
        """.stripMargin)
      System.exit(1)
    }

    val Array(bootstrapServers, subscribeType, topics) = args

    val spark = SparkSession
      .builder
      .appName("boontadata-spark-job1")
      .getOrCreate()

    import spark.implicits._

    // Create DataSet representing the stream of input lines from kafka
    val lines = spark
      .readStream
      .format("kafka")
      .option("kafka.bootstrap.servers", bootstrapServers)
      .option(subscribeType, topics)
      .load()
      .selectExpr("CAST(value AS STRING)")
      .as[String]

    // Generate running Word count
    val wordCounts = lines.flatMap(_.split(" ")).groupBy("value").count()

    // Start running the query that prints the running counts to the console
    val query = wordCounts.writeStream
      .outputMode("complete")
      .format("console")
      .start()

    query.awaitTermination()
  }

}

J'ai ajouté les fichiers sbt suivants: 

build.sbt: 

name := "boontadata-spark-job1"
version := "0.1"
scalaVersion := "2.11.7"

libraryDependencies += "org.Apache.spark" % "spark-core_2.11" % "2.0.2" % "provided"
libraryDependencies += "org.Apache.spark" % "spark-streaming_2.11" % "2.0.2" % "provided"
libraryDependencies += "org.Apache.spark" % "spark-sql_2.11" % "2.0.2" % "provided"
libraryDependencies += "org.Apache.spark" % "spark-sql-kafka-0-10_2.11" % "2.0.2"
libraryDependencies += "org.Apache.spark" % "spark-streaming-kafka-0-10_2.11" % "2.0.2"
libraryDependencies += "org.Apache.kafka" % "kafka-clients" % "0.10.1.1"
libraryDependencies += "org.Apache.kafka" % "kafka_2.11" % "0.10.1.1"

// META-INF discarding
assemblyMergeStrategy in Assembly := { 
   {
    case PathList("META-INF", xs @ _*) => MergeStrategy.discard
    case x => MergeStrategy.first
   }
}

J'ai aussi ajouté project/Assembly.sbt

addSbtPlugin("com.eed3si9n" % "sbt-Assembly" % "0.14.3")

Cela crée un pot Uber avec les pots non provided

Je soumets avec la ligne suivante: 

spark-submit boontadata-spark-job1-Assembly-0.1.jar ks1:9092,ks2:9092,ks3:9092 subscribe sampletopic

mais j'obtiens cette erreur d'exécution: 

Exception in thread "main" Java.lang.ClassNotFoundException: Failed to find data source: kafka. Please find packages at https://cwiki.Apache.org/confluence/display/SPARK/Third+Party+Projects
        at org.Apache.spark.sql.execution.datasources.DataSource.lookupDataSource(DataSource.scala:148)
        at org.Apache.spark.sql.execution.datasources.DataSource.providingClass$lzycompute(DataSource.scala:79)
        at org.Apache.spark.sql.execution.datasources.DataSource.providingClass(DataSource.scala:79)
        at org.Apache.spark.sql.execution.datasources.DataSource.sourceSchema(DataSource.scala:218)
        at org.Apache.spark.sql.execution.datasources.DataSource.sourceInfo$lzycompute(DataSource.scala:80)
        at org.Apache.spark.sql.execution.datasources.DataSource.sourceInfo(DataSource.scala:80)
        at org.Apache.spark.sql.execution.streaming.StreamingRelation$.apply(StreamingRelation.scala:30)
        at org.Apache.spark.sql.streaming.DataStreamReader.load(DataStreamReader.scala:124)
        at io.boontadata.spark.job1.DirectKafkaAggregateEvents$.main(StreamingJob.scala:41)
        at io.boontadata.spark.job1.DirectKafkaAggregateEvents.main(StreamingJob.scala)
        at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:62)
        at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43)
        at Java.lang.reflect.Method.invoke(Method.Java:498)
        at org.Apache.spark.deploy.SparkSubmit$.org$Apache$spark$deploy$SparkSubmit$$runMain(SparkSubmit.scala:736)
        at org.Apache.spark.deploy.SparkSubmit$.doRunMain$1(SparkSubmit.scala:185)
        at org.Apache.spark.deploy.SparkSubmit$.submit(SparkSubmit.scala:210)
        at org.Apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:124)
        at org.Apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)
Caused by: Java.lang.ClassNotFoundException: kafka.DefaultSource
        at Java.net.URLClassLoader.findClass(URLClassLoader.Java:381)
        at Java.lang.ClassLoader.loadClass(ClassLoader.Java:424)
        at Java.lang.ClassLoader.loadClass(ClassLoader.Java:357)
        at org.Apache.spark.sql.execution.datasources.DataSource$$anonfun$5$$anonfun$apply$1.apply(DataSource.scala:132)
        at org.Apache.spark.sql.execution.datasources.DataSource$$anonfun$5$$anonfun$apply$1.apply(DataSource.scala:132)
        at scala.util.Try$.apply(Try.scala:192)
        at org.Apache.spark.sql.execution.datasources.DataSource$$anonfun$5.apply(DataSource.scala:132)
        at org.Apache.spark.sql.execution.datasources.DataSource$$anonfun$5.apply(DataSource.scala:132)
        at scala.util.Try.orElse(Try.scala:84)
        at org.Apache.spark.sql.execution.datasources.DataSource.lookupDataSource(DataSource.scala:132)
        ... 18 more
16/12/23 13:32:48 INFO spark.SparkContext: Invoking stop() from shutdown hook

Existe-t-il un moyen de savoir quelle classe n’est pas trouvée afin que je puisse effectuer une recherche dans le dépôt de maven.org pour cette classe? 

Le code source lookupDataSource semble se trouver à la ligne 543 à https://github.com/Apache/spark/blob/83a6ace0d1be44f70e768348ae6688798c84343e/sql/core/src/main/scala/org/Apache/spark/spark/spark/spark/spark /DataSource.scala mais je n’ai pas trouvé de lien direct avec la source de données Kafka ...

Le code source complet est ici: https://github.com/boontadata/boontadata-streams/tree/ad0d0134ddb7664d359c8dca40f1d16dddd94053f

17
benjguin

J'ai essayé comme ça ça marche pour moi. Soumettez-le comme ceci et faites le moi savoir une fois que vous avez des problèmes

./spark-submit --packages org.Apache.spark:spark-sql-kafka-0-10_2.11:2.1.0 --class com.inndata.StructuredStreaming.Kafka --master local[*] /Users/Apple/.m2/repository/com/inndata/StructuredStreaming/0.0.1SNAPSHOT/StructuredStreaming-0.0.1-SNAPSHOT.jar
13
Sree Eedupuganti

Le problème est la section suivante dans build.sbt:

// META-INF discarding
assemblyMergeStrategy in Assembly := { 
   {
    case PathList("META-INF", xs @ _*) => MergeStrategy.discard
    case x => MergeStrategy.first
   }
}

Il indique que toutes les entrées META-INF doivent être supprimées, y compris le "code" qui permet aux alias de source de données (par exemple, kafka) de fonctionner.

Mais les fichiers META-INF sont très importants pour que kafka (et d’autres alias de sources de données en continu) fonctionne.

Pour que l'alias kafka fonctionne, Spark SQL utilise META-INF/services/org.Apache.spark.sql.sources.DataSourceRegister } avec l'entrée suivante:

org.Apache.spark.sql.kafka010.KafkaSourceProvider

KafkaSourceProviderest responsable de l'enregistrementkafka alias avec la source de données en continu appropriée, c'est-à-dire KafkaSource .

Juste pour vérifier que le code réel est effectivement disponible, mais que le "code" qui enregistre l'alias n'est pas enregistré, vous pouvez utiliser la source de données kafka sous le nom complet (et non l'alias) comme suit:

spark.readStream.
  format("org.Apache.spark.sql.kafka010.KafkaSourceProvider").
  load

Vous verrez d'autres problèmes dus à des options manquantes telles que kafka.bootstrap.servers, mais ...nous digressons.

Une solution consiste à MergeStrategy.concat all META-INF/services/org.Apache.spark.sql.sources.DataSourceRegister (qui créerait un uber-jar avec toutes les sources de données, y compris la source de données kafka).

case "META-INF/services/org.Apache.spark.sql.sources.DataSourceRegister" => MergeStrategy.concat
11
Jacek Laskowski

Dans mon cas, j'ai également eu cette erreur lors de la compilation avec sbt, et la cause en était que sbt Assembly n'incluait pas l'artefact spark-sql-kafka-0-10_2.11 dans le gros jar.

(Je serais très heureux de faire des commentaires ici. La dépendance n’étant pas une étendue, elle ne doit donc pas être considérée comme "fournie").

J'ai donc décidé de déployer un jar normal (slim) et d'inclure les dépendances avec les paramètres --jars dans spark-submit.

Afin de rassembler toutes les dépendances au même endroit, vous pouvez ajouter retrieveManaged := true à vos paramètres de projet sbt ou vous pouvez, dans la console sbt, émettre:

> set retrieveManaged := true
> package

Cela devrait amener toutes les dépendances dans le dossier lib_managed.

Ensuite, vous pouvez copier tous ces fichiers (avec une commande bash, vous pouvez par exemple utiliser quelque chose comme ceci

cd /path/to/your/project

JARLIST=$(find lib_managed -name '*.jar'| paste -sd , -)

spark-submit [other-args] target/your-app-1.0-SNAPSHOT.jar --jars "$JARLIST"
3
ssice

J'utilise spark 2.1 et je suis confronté au même problème, ma solution de contournement est la suivante: 

1) spark-Shell --packages org.Apache.spark:spark-sql-kafka-0-10_2.11:2.1.0

2) cd ~/.ivy2/jars vous y êtes, tous les fichiers nécessaires sont dans ce dossier

3) copier tous les fichiers JAR de ce dossier sur tous les nœuds (créer un dossier spécifique les contenant)

4) ajoutez le nom du dossier à spark.driver.extraClassPath et spark.driver.extraClassPath, par exemple. spark.driver.extraClassPath=/opt/jars/*:your_other_jars

5 spark-submit --class ClassNm --Other-Options YourJar.jar fonctionne bien maintenant

1
dalin qin

Cela tient compte de la réponse de Jacek Laskowski.

Ceux d'entre vous qui construisez votre projet sur maven peuvent essayer ceci . Ajoutez la ligne mentionnée ci-dessous à votre plug-in maven-shade 

META-INF/services/org.Apache.spark.sql.sources.DataSourceRegister

J'ai mis le code de plugin pour le fichier pom à titre d'exemple pour montrer où ajouter la ligne.


<plugin>
    <groupId>org.Apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.1.0</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer implementation="org.Apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>
                            META-INF/services/org.Apache.spark.sql.sources.DataSourceRegister
                        </resource>
                    </transformer>
                </transformers>
                <finalName>${project.artifactId}-${project.version}-uber</finalName>
            </configuration>
        </execution>
    </executions>
</plugin>

S'il vous plaît excuser mes compétences de mise en forme.

0
Algomeister

J'utilise gradle comme outil de construction et le plugin shadowJar pour créer le uberJar . La solution consistait simplement à ajouter un fichier. 

src/main/resources/META-INF/services/org.Apache.spark.sql.sources.DataSourceRegister  

au projet.

Dans ce fichier, vous devez indiquer, ligne par ligne, les noms de classe des sources de données que vous utilisez. Dans ce cas, ce serait org.Apache.spark.sql.kafka010.KafkaSourceProvider (recherchez ce nom de classe par exemple ici ).

La raison en est que Spark utilise Java ServiceLoader dans ses mécanismes internes de gestion des dépendances.

Exemple complet ici .

0
Falco Winkler

Je l'ai résolu en téléchargeant le fichier jar sur le système de pilotes. À partir de là, j'ai fourni le bocal à déclencher avec l'option --jar.

A noter également que je conditionnais tout l'environnement spark 2.1 dans mon uber jar (car mon cluster est toujours en 1.6.1).

spark-submit --jar /ur/path/spark-sql-kafka-0-10_2.11:2.1.0 --class ClassNm --Other-Options YourJar.jar

0
Gyan