Creación e implementación de una UDF mediante Lambda - Amazon Athena

Creación e implementación de una UDF mediante Lambda

Para crear una UDF personalizada, cree una nueva clase Java mediante la extensión de la clase UserDefinedFunctionHandler. El código fuente de UserDefinedFunctionHandler.java en el SDK está disponible en GitHub en el repositorio awslabs/aws-athena-query-federation/athena-federation-sdk, junto con implementaciones de UDF de ejemplo que puede examinar y modificar para crear una UDF personalizada.

Los pasos de esta sección muestran cómo escribir y crear un archivo Jar de UDF personalizado mediante Apache Maven desde la línea de comandos y una implementación.

Instrucciones para crear una UDF personalizada para Athena mediante Maven

Clonación del SDK y preparación del entorno de desarrollo

Antes de comenzar, asegúrese de que git esté instalado en su sistema mediante sudo yum install git -y.

Para instalar el SDK de federación de consultas de AWS
  • Escriba lo siguiente en la línea de comandos para clonar el repositorio de SDK. Este repositorio incluye el SDK, ejemplos y un conjunto de conectores de origen de datos. Para obtener más información sobre los conectores de origen de datos, consulte Uso de consulta federada de Amazon Athena.

    git clone https://github.com/awslabs/aws-athena-query-federation.git
Para instalar los requisitos previos para este procedimiento, tenga en cuenta lo siguiente:

Si está trabajando en un equipo de desarrollo que ya tiene instalado Apache Maven, la AWS CLI y la herramienta de compilación de AWS Serverless Application Model, puede omitir este paso.

  1. Desde la raíz del directorio aws-athena-query-federation que creó cuando realizó la clonación, ejecute el script prepare_dev_env.sh que prepara el entorno de desarrollo.

  2. Actualice el shell para generar nuevas variables creadas por el proceso de instalación o reinicie la sesión de terminal.

    source ~/.profile
    importante

    Si omite este paso, obtendrá errores más adelante acerca de que la herramienta de compilación de la AWS CLI o el AWS SAM no puede publicar su función de Lambda.

Creación del proyecto de Maven

Ejecute el siguiente comando para crear el proyecto de Maven. Reemplace groupId por el ID único de su organización y sustituya my-athena-udf por el nombre de su aplicación. Para obtener más información, consulte ¿Cómo realizo mi primer proyecto de Maven? en la documentación de Apache Maven.

mvn -B archetype:generate \ -DarchetypeGroupId=org.apache.maven.archetypes \ -DgroupId=groupId \ -DartifactId=my-athena-udfs

Adición de dependencias y complementos al proyecto de Maven

Agregue las siguientes configuraciones al archivo pom.xml del proyecto de Maven. Para ver un ejemplo, consulte el archivo pom.xml en GitHub.

<properties> <aws-athena-federation-sdk.version>2022.47.1</aws-athena-federation-sdk.version> </properties> <dependencies> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-athena-federation-sdk</artifactId> <version>${aws-athena-federation-sdk.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.1</version> <configuration> <createDependencyReducedPom>false</createDependencyReducedPom> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> </excludes> </filter> </filters> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> </execution> </executions> </plugin> </plugins> </build>

Escritura de código Java para las UDF

Extienda UserDefinedFunctionHandler.java para crear una nueva clase. Escriba sus UDF dentro de la clase.

En el siguiente ejemplo, se crean dos métodos Java para UDF compress() y decompress(), dentro de la clase MyUserDefinedFunctions.

*package *com.mycompany.athena.udfs; public class MyUserDefinedFunctions extends UserDefinedFunctionHandler { private static final String SOURCE_TYPE = "MyCompany"; public MyUserDefinedFunctions() { super(SOURCE_TYPE); } /** * Compresses a valid UTF-8 String using the zlib compression library. * Encodes bytes with Base64 encoding scheme. * * @param input the String to be compressed * @return the compressed String */ public String compress(String input) { byte[] inputBytes = input.getBytes(StandardCharsets.UTF_8); // create compressor Deflater compressor = new Deflater(); compressor.setInput(inputBytes); compressor.finish(); // compress bytes to output stream byte[] buffer = new byte[4096]; ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(inputBytes.length); while (!compressor.finished()) { int bytes = compressor.deflate(buffer); byteArrayOutputStream.write(buffer, 0, bytes); } try { byteArrayOutputStream.close(); } catch (IOException e) { throw new RuntimeException("Failed to close ByteArrayOutputStream", e); } // return encoded string byte[] compressedBytes = byteArrayOutputStream.toByteArray(); return Base64.getEncoder().encodeToString(compressedBytes); } /** * Decompresses a valid String that has been compressed using the zlib compression library. * Decodes bytes with Base64 decoding scheme. * * @param input the String to be decompressed * @return the decompressed String */ public String decompress(String input) { byte[] inputBytes = Base64.getDecoder().decode((input)); // create decompressor Inflater decompressor = new Inflater(); decompressor.setInput(inputBytes, 0, inputBytes.length); // decompress bytes to output stream byte[] buffer = new byte[4096]; ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(inputBytes.length); try { while (!decompressor.finished()) { int bytes = decompressor.inflate(buffer); if (bytes == 0 && decompressor.needsInput()) { throw new DataFormatException("Input is truncated"); } byteArrayOutputStream.write(buffer, 0, bytes); } } catch (DataFormatException e) { throw new RuntimeException("Failed to decompress string", e); } try { byteArrayOutputStream.close(); } catch (IOException e) { throw new RuntimeException("Failed to close ByteArrayOutputStream", e); } // return decoded string byte[] decompressedBytes = byteArrayOutputStream.toByteArray(); return new String(decompressedBytes, StandardCharsets.UTF_8); } }

Creación del archivo JAR

Ejecute mvn clean install para crear su proyecto. Después de que se compile correctamente, se crea un archivo JAR en la carpeta target de su proyecto denominada artifactId-version.jar, donde artifactId es el nombre que proporcionó en el proyecto de Maven, por ejemplo, my-athena-udfs.

Implementación de JAR en AWS Lambda

Tiene dos opciones para implementar su código en Lambda:

  • Implementación mediante AWS Serverless Application Repository (recomendado)

  • Creación de una función de Lambda desde el archivo JAR

Opción 1: implementación en el AWS Serverless Application Repository

Al implementar el archivo JAR en AWS Serverless Application Repository, se crea un archivo YAML de plantilla AWS SAM que representa la arquitectura de la aplicación. A continuación, especifique este archivo YAML y un bucket de Amazon S3 donde los artefactos de su aplicación se carguen y estén disponibles para el AWS Serverless Application Repository. El siguiente procedimiento utiliza el script publish.sh ubicado en el directorio athena-query-federation/tools del SDK de Athena Query Federation que clonó anteriormente.

Para obtener más información y requisitos, consulte Publicación de aplicaciones en la Guía para desarrolladores de AWS Serverless Application Repository, Conceptos de plantillas de AWS SAM en la Guía para desarrolladores de AWS Serverless Application Model y Publicación de aplicaciones sin servidor mediante la CLI de AWS SAM.

En el siguiente ejemplo se muestran los parámetros de un archivo YAML. Agregue parámetros similares a su archivo YAML y guárdelo en su directorio de proyecto. Consulte athena-udf.yaml en GitHub para ver un ejemplo completo.

Transform: 'AWS::Serverless-2016-10-31' Metadata: 'AWS::ServerlessRepo::Application': Name: MyApplicationName Description: 'The description I write for my application' Author: 'Author Name' Labels: - athena-federation SemanticVersion: 1.0.0 Parameters: LambdaFunctionName: Description: 'The name of the Lambda function that will contain your UDFs.' Type: String LambdaTimeout: Description: 'Maximum Lambda invocation runtime in seconds. (min 1 - 900 max)' Default: 900 Type: Number LambdaMemory: Description: 'Lambda memory in MB (min 128 - 3008 max).' Default: 3008 Type: Number Resources: ConnectorConfig: Type: 'AWS::Serverless::Function' Properties: FunctionName: !Ref LambdaFunctionName Handler: "full.path.to.your.handler. For example, com.amazonaws.athena.connectors.udfs.MyUDFHandler" CodeUri: "Relative path to your JAR file. For example, ./target/athena-udfs-1.0.jar" Description: "My description of the UDFs that this Lambda function enables." Runtime: java8 Timeout: !Ref LambdaTimeout MemorySize: !Ref LambdaMemory

Copie el script publish.sh en el directorio del proyecto donde guardó el archivo YAML y ejecute el siguiente comando:

./publish.sh MyS3Location MyYamlFile

Por ejemplo, si la ubicación del bucket es s3://amzn-s3-demo-bucket/mysarapps/athenaudf y el archivo YAML se guardó como my-athena-udfs.yaml:

./publish.sh amzn-s3-demo-bucket/mysarapps/athenaudf my-athena-udfs
Cómo crear una función de Lambda
  1. Abra la consola de Lambda en https://console.aws.amazon.com/lambda/, elija Create function (Crear función), y luego elija Browse serverless app repository (Explorar el repositorio de aplicaciones sin servidor).

  2. Elija Private applications (Aplicaciones privadas) busque su aplicación en la lista o búsquela con palabras clave y selecciónela.

  3. Revise y proporcione los detalles de la aplicación y, a continuación, elija Deploy (Implementar).

    Ahora puede utilizar los nombres de los métodos definidos en su archivo JAR de la función de Lambda como UDF en Athena.

Opción 2: creación directa de una función de Lambda

También puede crear una función de Lambda directamente mediante la consola o la AWS CLI. En el siguiente ejemplo, se muestra el uso del comando de la CLI create-function de Lambda.

aws lambda create-function \ --function-name MyLambdaFunctionName \ --runtime java8 \ --role arn:aws:iam::1234567890123:role/my_lambda_role \ --handler com.mycompany.athena.udfs.MyUserDefinedFunctions \ --timeout 900 \ --zip-file fileb://./target/my-athena-udfs-1.0-SNAPSHOT.jar