Condividi tramite


Usare un file JAR in un processo di Azure Databricks

Il formato di file Java o [JAR](https://en.wikipedia.org/wiki/JAR_(file_format) si basa sul formato di file ZIP più diffuso e viene usato per aggregare in un solo file multipli file Java o Scala. Usando l'attività JAR, è possibile garantire un'installazione rapida e affidabile del codice Java o Scala nei processi di Azure Databricks. Questo articolo fornisce un esempio di creazione di un file JAR e di un processo che esegue l'applicazione in pacchetto nel file JAR. Nel seguente esempio, si eseguirà quanto segue:

  • Creare il progetto JAR che definisce un'applicazione di esempio.
  • Aggregare i file di esempio in un file JAR.
  • Per eseguire il file JAR creare un processo.
  • Eseguire il processo e visualizzare i risultati.

Prima di iniziare

Per completare questo esempio, è necessario quanto segue:

  • Per Java JAR, Java Development Kit (JDK).
  • Per i JAR Scala, JDK e sbt.

Passaggio 1: Creare una directory locale per l'esempio

Creare una directory locale per contenere il codice di esempio e gli artefatti generati, ad esempio databricks_jar_test.

Passaggio 2: Creare il JAR

Completare le istruzioni seguenti per usare Java o Scala per creare il file JAR.

Creare un Java JAR

  1. Nella cartella databricks_jar_test creare un file denominato PrintArgs.java con il seguente contenuto:

    import java.util.Arrays;
    
    public class PrintArgs {
      public static void main(String[] args) {
        System.out.println(Arrays.toString(args));
      }
    }
    
  2. Compilare il file PrintArgs.java, che crea il file PrintArgs.class:

    javac PrintArgs.java
    
  3. (Facoltativo) Eseguire il programma compilato:

    java PrintArgs Hello World!
    
    # [Hello, World!]
    
  4. Nella stessa cartella dei file PrintArgs.java e PrintArgs.class creare una cartella denominata META-INF.

  5. Nella cartella META-INF creare un file denominato MANIFEST.MF con il seguente contenuto. Assicurarsi di aggiungere una nuova riga alla fine di questo file:

    Main-Class: PrintArgs
    
  6. Dalla radice della cartella databricks_jar_test creare un file JAR denominato PrintArgs.jar:

    jar cvfm PrintArgs.jar META-INF/MANIFEST.MF *.class
    
  7. (Facoltativo) Per testarlo, dalla radice della cartella databricks_jar_test, eseguire il file JAR:

    java -jar PrintArgs.jar Hello World!
    
    # [Hello, World!]
    

    Nota

    Se viene visualizzato l'errore no main manifest attribute, in PrintArgs.jar, assicurarsi di aggiungere una nuova riga alla fine del file MANIFEST.MF e quindi provare a creare ed eseguire di nuovo il file JAR.

  8. Caricare PrintArgs.jar in un volume. Vedere Caricamento file in un volume del catalogo Unity.

Creare un file JAR Scala

  1. Nella cartella databricks_jar_test creare un file vuoto denominato build.sbt con il seguente contenuto:

    ThisBuild / scalaVersion := "2.12.14"
    ThisBuild / organization := "com.example"
    
    lazy val PrintArgs = (project in file("."))
      .settings(
        name := "PrintArgs"
      )
    
  2. databricks_jar_test Dalla cartella creare la struttura src/main/scala/exampledi cartelle .

  3. Nella cartella example creare un file denominato PrintArgs.scala con il seguente contenuto:

    package example
    
    object PrintArgs {
      def main(args: Array[String]): Unit = {
        println(args.mkString(", "))
      }
    }
    
  4. Compilare il programma:

    sbt compile
    
  5. (Facoltativo) Eseguire il programma compilato:

    sbt "run Hello World\!"
    
    # Hello, World!
    
  6. Nella cartella databricks_jar_test/project creare un file denominato assembly.sbt con il seguente contenuto:

    addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.0.0")
    
  7. Dalla radice della cartella databricks_jar_test eseguire il comando assembly, che genera un file JAR nella cartella target:

    sbt assembly
    
  8. (Facoltativo) Per testarlo, dalla radice della cartella databricks_jar_test, eseguire il file JAR:

    java -jar target/scala-2.12/PrintArgs-assembly-0.1.0-SNAPSHOT.jar Hello World!
    
    # Hello, World!
    
  9. Caricare PrintArgs-assembly-0.1.0-SNAPSHOT.jar in un volume. Vedere Caricamento file in un volume del catalogo Unity.

Passaggio 3. Creare un processo di Azure Databricks per eseguire il file JAR

  1. Passare alla pagina di destinazione di Azure Databricks ed eseguire una delle operazioni seguenti:
    • Nella barra laterale, fare clic su Icona Flussi di lavoro Flussi di lavoro e quindi Pulsante Crea processo.
    • Nella barra laterale, fare clic su Nuova icona Nuovo e selezionare Processo dal menu.
  2. Nella finestra di dialogo delle attività visualizzata nella scheda Attività sostituire Aggiungi un nome per il processo... con il nome del processo, ad esempio JAR example.
  3. Per Nome attività immettere un nome per l'attività, ad esempio java_jar_task per Java o scala_jar_task per Scala.
  4. In Tipo selezionare JAR.
  5. Per Classe principale, per questo esempio, immettere PrintArgs per Java o example.PrintArgs per Scala.
  6. Per Cluster selezionare un cluster compatibile. Vedere Supporto per le librerie Java e Scala.
  7. Per Librerie dipendenti fare clic su + Aggiungi.
  8. Nella finestra di dialogo Aggiungi libreria dipendente, con Volumi selezionati, immettere il percorso in cui è stato caricato il file JAR (PrintArgs.jar o PrintArgs-assembly-0.1.0-SNAPSHOT.jar) nel passaggio precedente in Percorso file volumi oppure filtrare o cercare il file JAR. Selezionarlo.
  9. Fare clic su Aggiungi.
  10. Per Parametri, per questo esempio, immettere ["Hello", "World!"].
  11. Fare clic su Aggiungi.

Passaggio 4: Eseguire il processo e visualizzare i dettagli dell'esecuzione del processo

Fare clic su Pulsante Esegui ora per eseguire il flusso di lavoro. Per visualizzare i dettagli dell'esecuzione, fare clic su Visualizza esecuzione nella finestra popup Esecuzione attivata oppure fare clic sul collegamento nella colonna Ora di inizio per l'esecuzione nella visualizzazioneesecuzioni del processo.

Al termine dell'esecuzione, l'output viene visualizzato nel pannello Output, inclusi gli argomenti passati all'attività.

Limiti delle dimensioni di output per i processi JAR

L'output del processo, ad esempio l'output del log generato in stdout, è soggetto a un limite di dimensioni di 20 MB. Se l'output totale ha dimensioni maggiori, l'esecuzione viene annullata e contrassegnata come non riuscita.

Per evitare di riscontrare questo limite, è possibile impedire che stdout venga restituito dal driver ad Azure Databricks impostando la configurazione di Spark spark.databricks.driver.disableScalaOutput su true. Per impostazione predefinita, il valore del flag è false. Il flag controlla l'output della cella per i processi JAR scala e i notebook Scala. Se il flag è abilitato, Spark non restituisce i risultati dell'esecuzione del processo al client. Il flag non influisce sui dati scritti nei file di log del cluster. Databricks consiglia di impostare questo flag solo per i cluster automatizzati per i processi JAR, perché disabilita i risultati del notebook.

Raccomandazione: usare la condivisione SparkContext

Poiché Azure Databricks è un servizio gestito, potrebbero essere necessarie alcune modifiche al codice per garantire che i processi Apache Spark vengano eseguiti correttamente. I programmi di processo JAR devono usare l'API condivisa SparkContext per ottenere SparkContext. Poiché Azure Databricks inizializza SparkContext, i programmi che richiamano new SparkContext() avranno esito negativo. Per ottenere SparkContext, usare solo l'oggetto condiviso SparkContext creato da Azure Databricks:

val goodSparkContext = SparkContext.getOrCreate()
val goodSparkSession = SparkSession.builder().getOrCreate()

Esistono anche diversi metodi da evitare quando si usa l'oggetto condiviso SparkContext.

  • Non chiamare SparkContext.stop().
  • Non chiamare System.exit(0) o sc.stop() alla fine del programma Main. Ciò potrebbe causare comportamenti inaspettati.

Raccomandazione: usare i blocchi try-finally per la pulizia del processo

Si consideri un file JAR costituito da due parti:

  • jobBody() che contiene la parte principale del processo.
  • jobCleanup() che deve essere eseguito dopo jobBody(), indipendentemente dal fatto che tale funzione abbia avuto esito positivo o restituito un'eccezione.

Ad esempio, jobBody() crea tabelle e jobCleanup() elimina tali tabelle.

Il modo sicuro per assicurarsi che venga chiamato il metodo di pulizia consiste nell'inserire un blocco try-finally nel codice:

try {
  jobBody()
} finally {
  jobCleanup()
}

Non è consigliabile provare a eseguire la pulizia usando sys.addShutdownHook(jobCleanup) o il codice seguente:

val cleanupThread = new Thread { override def run = jobCleanup() }
Runtime.getRuntime.addShutdownHook(cleanupThread)

A causa del modo in cui la durata dei contenitori Spark viene gestita in Azure Databricks, gli hook di arresto non vengono eseguiti in modo affidabile.

Configurazione dei parametri del processo JAR

I parametri passano ai processi JAR con una matrice di stringhe JSON. Vedere l'oggetto spark_jar_task nel corpo della richiesta passato all'operazione Crea un nuovo processo (POST /jobs/create) nell'API Processi. Per accedere a questi parametri, esaminare la matrice String passata nella funzione main.

Gestione delle dipendenze della libreria

Il driver Spark presenta alcune dipendenze della libreria che non possono essere sottoposte a override. Se il processo aggiunge librerie in conflitto, le dipendenze della libreria del driver Spark hanno la precedenza.

Per ottenere l'elenco completo delle dipendenze della libreria del driver, eseguire il comando seguente in un notebook collegato a un cluster configurato con la stessa versione di Spark (o il cluster con il driver da esaminare):

%sh
ls /databricks/jars

Quando si definiscono le dipendenze della libreria per i file JAR, Databricks consiglia di elencare Spark e Hadoop come provided dipendenze. In Maven aggiungere Spark e Hadoop come dipendenze fornite:

<dependency>
  <groupId>org.apache.spark</groupId>
  <artifactId>spark-core_2.11</artifactId>
  <version>2.3.0</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.apache.hadoop</groupId>
  <artifactId>hadoop-core</artifactId>
  <version>1.2.1</version>
  <scope>provided</scope>
</dependency>

In sbt aggiungere Spark e Hadoop come dipendenze fornite:

libraryDependencies += "org.apache.spark" %% "spark-core" % "2.3.0" % "provided"
libraryDependencies += "org.apache.hadoop" %% "hadoop-core" % "1.2.1" % "provided"

Suggerimento

Specificare la versione di Scala corretta per le dipendenze in base alla versione in esecuzione.

Passaggi successivi

Per altre informazioni sulla creazione e l'esecuzione di processi di Azure Databricks, vedere Pianificare e orchestrare i flussi di lavoro.