Opciones de implementación de Java

Tienes dos opciones para desplegar una función de Java:

Desplegar desde el código fuente

El código fuente de tu función debe estar en la ubicación habitual de los proyectos de Maven (src/main/java). Las funciones de ejemplo de este documento se encuentran directamente en src/main/java, sin ninguna declaración de paquete en el archivo de origen .java. En el caso de código no trivial, probablemente introducirías un paquete. Si el paquete es com.example, la jerarquía sería la siguiente:

myfunction/
├─ pom.xml
├─ src
    ├─main
        ├─ java
            ├─ com
                ├─ example
                    ├─ MyFunction.java

Usa el siguiente comando para desplegar una función HTTP:

gcloud functions deploy $name --trigger-http --no-gen2 \
    --entry-point $function_class --runtime java17

Donde:

  • $name es un nombre descriptivo arbitrario que será el nombre de la función una vez implementada. $name solo puede contener letras, números, guiones bajos y guiones.
  • $function_class es el nombre completo de tu clase (por ejemplo, com.example.MyFunction o simplemente MyFunction si no usas ningún paquete).

Usa el siguiente comando para desplegar una función basada en eventos:

gcloud functions deploy $name --no-gen2 --entry-point $function_class \
    --trigger-resource $resource_name \
    --trigger-event $event_name \
    --runtime java17

Donde:

  • $name es un nombre descriptivo arbitrario que será el nombre de la función una vez implementada.
  • $function_class es el nombre completo de tu clase (por ejemplo, com.example.MyFunction o simplemente MyFunction si no usas ningún paquete).
  • $resource_name y $event_name son específicos de los eventos que activan tu función. Algunos ejemplos de recursos y eventos admitidos son Google Cloud Pub/Sub y Google Cloud Storage.

Cuando se despliega una función desde el código fuente, la CLI de Google Cloud sube el directorio de origen (y todo lo que contiene) a Google Cloud para compilarlo. Para evitar enviar archivos innecesarios, puedes usar el archivo .gcloudignore . Edita el archivo .gcloudignore para ignorar directorios comunes como .git y target/. Por ejemplo, un archivo .gcloudignore podría contener lo siguiente:

.git
target
build
.idea

Desplegar desde un archivo JAR

Puedes implementar un archivo JAR prediseñado que contenga la función. Esto resulta útil, sobre todo si necesitas desplegar una función que usa dependencias de un repositorio de artefactos privado al que no se puede acceder desde la canalización de compilación de Google Cloud al compilar desde el origen. El archivo JAR puede ser un uber JAR que contenga la clase de la función y todas sus clases de dependencia, o bien un archivo JAR ligero que tenga entradas Class-Path para los archivos JAR de dependencia en el archivo META-INF/MANIFEST.MF.

Compilar y desplegar un Uber JAR

Un uber JAR es un archivo JAR que contiene las clases de funciones, así como todas sus dependencias. Puedes crear un uber JAR con Maven y Gradle. Para desplegar un Uber JAR, debe ser el único archivo JAR de su directorio. Por ejemplo:

my-function-deployment/
 ├─ my-function-with-all-dependencies.jar

Puedes copiar el archivo en esta estructura de directorios o usar los complementos de Maven y Gradle para generar el directorio de implementación correcto.

Maven

Usa el complemento Maven Shade para crear un archivo JAR grande. Configura tu pom.xml con el complemento Shade:

<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...
  <build>
    ...
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <executions>
          <execution>
            <phase>package</phase>
            <goals><goal>shade</goal></goals>
            <configuration>
              <outputFile>${project.build.directory}/deployment/${build.finalName}.jar</outputFile>
              <transformers>
                <!-- This may be needed if you need to shade a signed JAR -->
                <transformer implementation="org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer">
                  <resource>.SF</resource>
                  <resource>.DSA</resource>
                  <resource>.RSA</resource>
                </transformer>
                <!-- This is needed if you have dependencies that use Service Loader. Most Google Cloud client libraries does. -->
                <transformer implementation=
       "org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

Crea el uber JAR:

mvn package

A continuación, haz el despliegue con el siguiente comando:

gcloud functions deploy jar-example \
    --entry-point=Example \
    --no-gen2 \
    --runtime=java17 \
    --trigger-http \
    --source=target/deployment

Gradle

Usa el complemento Shadow para Gradle. Configura el complemento en el archivo build.gradle:

buildscript {
   repositories {
       jcenter()
   }
   dependencies {
       ...
       classpath "com.github.jengelman.gradle.plugins:shadow:5.2.0"
   }
}

plugins {
   id 'java'
   ...
}
sourceCompatibility = '17.0'
targetCompatibility = '17.0'
apply plugin: 'com.github.johnrengelman.shadow'

shadowJar {
   mergeServiceFiles()
}
...

Ahora puedes ejecutar Gradle con el comando shadowJar:

gradle shadowJar

A continuación, haz el despliegue con el siguiente comando:

gcloud functions deploy jar-example \
   --entry-point=Example \
   --no-gen2 \
   --runtime=java17 \
   --trigger-http \
   --source=build/libs

Compilar y desplegar un archivo JAR ligero con dependencias externas

Puedes compilar y desplegar un archivo JAR ligero en lugar de un uber JAR. Un archivo JAR ligero es un archivo JAR que contiene solo las clases de funciones sin las dependencias insertadas en el mismo archivo JAR. Como las dependencias siguen siendo necesarias para la implementación, debes configurar los elementos de la siguiente manera:

  • Las dependencias deben estar en un subdirectorio relativo al archivo JAR que se va a implementar.
  • El archivo JAR debe tener un archivo META-INF/MANIFEST.MF que incluya un atributo Class-Path cuyo valor enumere las rutas de dependencia necesarias.

Por ejemplo, tu archivo JAR my-function.jar tiene un archivo META-INF/MANIFEST.MF que tiene 2 dependencias en el directorio libs/ (una lista de rutas relativas separadas por espacios):

Manifest-Version: 1.0
Class-Path: libs/dep1.jar libs/dep2.jar

El directorio de implementación debe contener el archivo JAR de la función principal, así como un subdirectorio con las dos dependencias de las que depende la función:

function-deployment/
├─ my-function.jar
├─ libs
       ├─ dep1.jar
       ├─ dep2.jar

Puedes crear un archivo JAR ligero con Maven y Gradle:

Maven

Usa el complemento JAR de Maven para configurar automáticamente MANIFEST.MF con las rutas de las dependencias y, a continuación, usa el complemento de dependencia de Maven para copiar las dependencias.

<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...
  <build>
    ...
    <plugins>
      <plugin>
        <artifactId>maven-jar-plugin</artifactId>
        <configuration>
          <archive>
            <manifest>
              <addClasspath>true</addClasspath>
              <classpathPrefix>libs/</classpathPrefix>
            </manifest>
          </archive>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
              <overWriteReleases>false</overWriteReleases>
              <includeScope>runtime</includeScope>
              <outputDirectory>${project.build.directory}/libs</outputDirectory>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-resources-plugin</artifactId>
        <executions>
          <execution>
            <id>copy-resources</id>
            <phase>package</phase>
            <goals><goal>copy-resources</goal></goals>
            <configuration>
              <outputDirectory>${project.build.directory}/deployment</outputDirectory>
              <resources>
                <resource>
                  <directory>${project.build.directory}</directory>
                  <includes>
                    <include>${build.finalName}.jar</include>
                    <include>libs/**</include>
                  </includes>
                  <filtering>false</filtering>
                </resource>
              </resources>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

Compila el JAR delgado:

mvn package

A continuación, haz el despliegue con el siguiente comando:

gcloud functions deploy jar-example \
    --entry-point=Example \
    --no-gen2 \
    --runtime=java17 \
    --trigger-http \
    --source=target/deployment

Gradle

Actualiza el archivo de proyecto build.gradle para añadir una nueva tarea que obtenga las dependencias:

dependencies {
   // API available at compilation only, but provided at runtime
   compileOnly 'com.google.cloud.functions:functions-framework-api:1.0.1'
   // dependencies needed by the function
   // ...
}

jar {
 manifest {
   attributes(
     "Class-Path": provider {
         configurations.runtimeClasspath
           .collect { "libs/${it.name}" }.join(' ')
     }
   )
 }
}

task prepareDeployment(type: Copy) {
 into("${buildDir}/deployment")
 into('.') {
   from jar
 }
 into('libs') {
   from configurations.runtimeClasspath
 }
}