Uso de un archivo JAR en un trabajo de Azure Databricks

El formato de archivo de archivos Java o [JAR](https://en.wikipedia.org/wiki/JAR_(file_format)) se basa en el popular formato de archivo ZIP y se usa para agrupar muchos archivos Java o Scala en uno solo. Con la tarea de JAR, puede garantizar una instalación rápida y confiable de código de Java o Scala en los trabajos de Azure Databricks. En este artículo, se proporciona un ejemplo de cómo crear un archivo JAR y un trabajo que ejecute la aplicación empaquetada en el archivo JAR. En este ejemplo, hará lo siguiente:

  • Crear el proyecto de JAR que define una aplicación de ejemplo
  • Agrupar los archivos de ejemplo en un archivo JAR
  • Crear un trabajo para ejecutar el archivo JAR
  • Ejecutar el trabajo y ver los resultados

Antes de empezar

Para completar este ejemplo, necesita lo siguiente:

  • Para los archivos JAR de Java, el kit de desarrollo de Java (JDK).
  • Para los archivos JAR de Scala, el JDK y sbt.

Paso 1: Crear un directorio local para el ejemplo

Cree un directorio local para incluir en este el código de ejemplo y los artefactos generados, por ejemplo, databricks_jar_test.

Paso 2: Crear el archivo JAR

Complete las instrucciones siguientes para usar Java o Scala para crear el archivo JAR.

Creación de un archivo JAR de Java

  1. En la carpeta databricks_jar_test, cree un archivo denominado PrintArgs.java con el contenido siguiente:

    import java.util.Arrays;
    
    public class PrintArgs {
      public static void main(String[] args) {
        System.out.println(Arrays.toString(args));
      }
    }
    
  2. Compile el archivo PrintArgs.java, lo que crea el archivo PrintArgs.class:

    javac PrintArgs.java
    
  3. (Opcional) Ejecute el programa compilado:

    java PrintArgs Hello World!
    
    # [Hello, World!]
    
  4. En la misma carpeta que los archivos PrintArgs.java y PrintArgs.class, cree una carpeta denominada META-INF.

  5. En la carpeta META-INF, cree un archivo denominado MANIFEST.MF con el contenido siguiente. Asegúrese de agregar una nueva línea al final de este archivo:

    Main-Class: PrintArgs
    
  6. En la raíz de la carpeta databricks_jar_test, cree un archivo JAR denominado PrintArgs.jar:

    jar cvfm PrintArgs.jar META-INF/MANIFEST.MF *.class
    
  7. (Opcional) Para probarlo, desde la raíz de la carpeta databricks_jar_test, ejecute el archivo JAR:

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

    Nota:

    Si recibe el error no main manifest attribute, in PrintArgs.jar, asegúrese de agregar una nueva línea al final del archivo MANIFEST.MF e intente crear y ejecutar nuevamente el archivo JAR.

  8. Cargue PrintArgs.jar en un volumen. Consulte Cargar archivos en un volumen de Unity Catalog.

Creación de un archivo JAR de Scala

  1. En la carpeta databricks_jar_test, cree un archivo vacío denominado build.sbt con el contenido siguiente:

    ThisBuild / scalaVersion := "2.12.14"
    ThisBuild / organization := "com.example"
    
    lazy val PrintArgs = (project in file("."))
      .settings(
        name := "PrintArgs"
      )
    
  2. En la carpeta databricks_jar_test, cree la estructura de carpeta src/main/scala/example.

  3. En la carpeta example, cree un archivo denominado PrintArgs.scala con el contenido siguiente:

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

    sbt compile
    
  5. (Opcional) Ejecute el programa compilado:

    sbt "run Hello World\!"
    
    # Hello, World!
    
  6. En la carpeta databricks_jar_test/project, cree un archivo denominado assembly.sbt con el contenido siguiente:

    addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.0.0")
    
  7. Desde la raíz de la carpeta databricks_jar_test, ejecute el comando assembly, que genera un ARCHIVO JAR en la carpeta target:

    sbt assembly
    
  8. (Opcional) Para probarlo, desde la raíz de la carpeta databricks_jar_test, ejecute el archivo JAR:

    java -jar target/scala-2.12/PrintArgs-assembly-0.1.0-SNAPSHOT.jar Hello World!
    
    # Hello, World!
    
  9. Cargue PrintArgs-assembly-0.1.0-SNAPSHOT.jar en un volumen. Consulte Cargar archivos en un volumen de Unity Catalog.

Paso 3. Crear un trabajo de Azure Databricks para ejecutar el archivo JAR

  1. Vaya a la página de aterrizaje de Azure Databricks y realice una de las siguientes acciones:
    • En la barra lateral, haga clic en Icono de trabajosFlujos de trabajo y haga clic en elBotón Crear trabajo.
    • En la barra lateral, haga clic en Icono NuevoNuevo y seleccione Trabajo del menú.
  2. En el cuadro de diálogo de tarea que aparece en la pestaña Tareas, reemplace Agregar un nombre para el trabajo... por el nombre del trabajo, por ejemplo, JAR example.
  3. En el Nombre de tarea, escriba un nombre para la tarea; por ejemplo, java_jar_task para Java o scala_jar_task para Scala.
  4. En Tipo, seleccione JAR.
  5. En Clase principal, escriba, por ejemplo, PrintArgs para Java o example.PrintArgs para Scala.
  6. En Clúster, seleccione un clúster compatible. Consulte Compatibilidad con bibliotecas de Java y Scala.
  7. En Bibliotecas dependientes, haga clic en + Agregar.
  8. En el cuadro de diálogo Agregar biblioteca dependiente, con Volúmenes seleccionados, escriba la ubicación donde cargó el archivo JAR (PrintArgs.jar o PrintArgs-assembly-0.1.0-SNAPSHOT.jar) en el paso anterior en Ruta de acceso del archivo de volúmenes o filtre o busque el archivo JAR. Selecciónelo.
  9. Haga clic en Agregar.
  10. En Parámetros, escriba ["Hello", "World!"] para este ejemplo.
  11. Haga clic en Agregar.

Paso 4: Ejecutar el trabajo y ver los detalles de ejecución del trabajo

Haga clic en botón Ejecutar ahora para ejecutar el flujo de trabajo. Para ver los detalles de la ejecución, haga clic en Ver ejecución en el menú emergente Ejecución desencadenada o haga clic en el vínculo de la columna Hora de inicio de la ejecución en la vista de ejecuciones de trabajos.

Una vez completada la ejecución, se muestra la salida en el panel Salida, incluidos los argumentos pasados a la tarea.

Límites de tamaño de salida para los trabajos JAR

La salida de trabajos, como la salida del registro que se emite a stdout, está sujeta a un límite de tamaño de 20 MB. Si la salida total tiene un tamaño mayor, la ejecución se cancela y se marca como con errores.

Para evitar alcanzar este límite, puede impedir que stdout se devuelva desde el controlador a Azure Databricks estableciendo la configuración para Spark de spark.databricks.driver.disableScalaOutput en true. De forma predeterminada, el valor de marca es false. La marca permite controlar la salida de celda para los trabajos JAR y los cuadernos de Scala. Si la marca está habilitada, Spark no devuelve los resultados de la ejecución del trabajo al cliente. La marca no afecta a los datos escritos en los archivos de registro del clúster. Databricks recomienda establecer esta marca solo para clústeres de trabajos para trabajos JAR porque deshabilita los resultados del cuaderno.

Recomendación: Usar el recurso compartido SparkContext

Dado que Azure Databricks es un servicio administrado, es posible que sea necesario realizar algunos cambios en el código para garantizar que los trabajos de Apache Spark se ejecuten correctamente. Los programas de trabajo de JAR deben usar la API SparkContext compartida para obtener el SparkContext. Dado que Azure Databricks inicializa SparkContext, se producirá un error en los programas que invocan new SparkContext(). Para obtener SparkContext, use solo el objeto compartido SparkContext creado por Azure Databricks:

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

También hay varios métodos que debe evitar al usar el objeto compartido SparkContext.

  • No llame a SparkContext.stop().
  • No llame a System.exit(0) ni a sc.stop() al final del programa Main. Esto puede provocar un comportamiento indefinido.

Recomendación: Uso de try-finally bloques para la limpieza de trabajos

Considere un archivo JAR que consta de dos partes:

  • jobBody(), que contiene la parte principal del trabajo.
  • jobCleanup() que se debe ejecutar después de jobBody(), si esa función se ha ejecutado correctamente o ha devuelto una excepción.

Por ejemplo, jobBody() crea tablas y jobCleanup() quite esas tablas.

La mejor manera de asegurarse de que se llama al método de limpieza es colocar un bloque try-finally en el código:

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

No debe intentar limpiar con sys.addShutdownHook(jobCleanup) o con el código siguiente:

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

Dado el modo en que se administra la vida útil de los contenedores Spark en Azure Databricks, los ganchos de apagado no se ejecutan de forma fiable.

Configuración de los parámetros del trabajo JAR

Los parámetros se pasan a trabajos JAR con una matriz de cadenas JSON. Vea el objeto spark_jar_task en el cuerpo de la solicitud pasado a la operación Create a new job (Crear un trabajo) (POST /jobs/create) en la API de trabajos. Para acceder a estos parámetros, inspeccione la matriz String que se pasa a la función main.

Administración de dependencias de biblioteca

El controlador de Spark tiene determinadas dependencias de biblioteca que no se pueden invalidar. Si el trabajo agrega bibliotecas en conflicto, las dependencias de la biblioteca de controladores de Spark tienen prioridad.

Para obtener la lista completa de las dependencias de la biblioteca de controladores, ejecute el siguiente comando en un bloc de notas conectado a un clúster configurado con la misma versión de Spark (o el clúster con el controlador que desea examinar):

%sh
ls /databricks/jars

Al definir dependencias de biblioteca para JAR, Databricks recomienda enumerar Spark y Hadoop como provided dependencias. En Maven, agregue Spark y Hadoop como dependencias proporcionadas:

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

Ensbt, agregue Spark y Hadoop como dependencias proporcionadas:

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

Sugerencia

Especifique la versión correcta de Scala para las dependencias en función de la versión que esté ejecutando.

Pasos siguientes

Para obtener más información sobre la creación y ejecución de trabajos de Azure Databricks, consulte Creación y ejecución de trabajos de Azure Databricks.