Spring Cloud Function en Azure

Este artículo le explica cómo usar Spring Cloud Function para desarrollar una función de Java y publicarla en Azure Functions. Cuando haya terminado, el código de la función se ejecuta en el Plan de consumo en Azure y puede activarse mediante una solicitud HTTP.

Si no tiene una suscripción a Azure, cree una cuenta gratuita antes de empezar.

Requisitos previos

Para desarrollar funciones con Java, debe tener instalado lo siguiente:

Importante

  1. Debe establecer la JAVA_HOME variable de entorno en la ubicación de instalación del JDK para completar este inicio rápido.
  2. Asegúrese de que la versión de las herramientas principales sea al menos 4.0.5455.

Qué vamos a crear

Vamos a crear una función clásica "Hello, World" que se ejecuta en Azure Functions y se configura con Spring Cloud Function.

La función recibe un User objeto JSON, que contiene un nombre de usuario, y devuelve un Greeting objeto , que contiene el mensaje de bienvenida a ese usuario.

El proyecto está disponible en el ejemplo de Spring Cloud Function en Azure del repositorio azure-function-java-worker en GitHub. Puede usar ese ejemplo directamente si desea ver el trabajo final descrito en este inicio rápido.

Creación de un nuevo proyecto de Maven

Vamos a crear un proyecto de Maven vacío y a configurarlo con Spring Cloud Function y Azure Functions.

En una carpeta vacía, cree un nuevo archivo pom.xml y copie y pegue el contenido del proyecto de ejemplo en el archivo pom.xml.

Nota:

Este archivo usa las dependencias de Maven tanto de Spring Boot como de Spring Cloud Function, y configura los complementos de Maven para Spring Boot y Azure Functions.

Debe personalizar algunas propiedades para la aplicación:

  • <functionAppName> es el nombre de la función de Azure
  • <functionAppRegion> es el nombre de la región de Azure donde se implementa la función.
  • <functionResourceGroup> es el nombre del grupo de recursos de Azure que usa.

Cambie esas propiedades directamente cerca de la parte superior del archivo pom.xml, como se muestra en el ejemplo siguiente:

    <properties>
        <java.version>11</java.version>

        <!-- Spring Boot start class. WARNING: correct class must be set -->
        <start-class>com.example.DemoApplication</start-class>

        <!-- customize those properties. WARNING: the functionAppName should be unique across Azure -->
        <azure.functions.maven.plugin.version>1.29.0</azure.functions.maven.plugin.version>
        <functionResourceGroup>my-spring-function-resource-group</functionResourceGroup>
        <functionAppServicePlanName>my-spring-function-service-plan</functionAppServicePlanName>
        <functionAppName>my-spring-function</functionAppName>
        <functionPricingTier>Y1</functionPricingTier>
        <functionAppRegion>eastus</functionAppRegion>
    </properties>

Creación de archivos de configuración de Azure

Cree una carpeta src/main/resources y agregue los siguientes archivos de configuración de Azure Functions.

host.json:

{
  "version": "2.0",
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[4.*, 5.2.0)"
  },
  "functionTimeout": "00:10:00"
}

local.settings.json:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "",
    "FUNCTIONS_WORKER_RUNTIME": "java",
    "FUNCTIONS_EXTENSION_VERSION": "~4",
    "AzureWebJobsDashboard": ""
  }
}

Creación de objetos de dominio

Azure Functions puede recibir y enviar objetos en formato JSON. Ahora vamos a crear los objetos User y Greeting, que representan nuestro modelo de dominio. Puede crear objetos más complejos, con más propiedades, si desea personalizar este inicio rápido y hacerlo más interesante para su caso.

Cree una carpetasrc/main/java/com/example/model y agregue los dos archivos siguientes:

User.java:

package com.example.model;

public class User {

    private String name;

    public User() {
    }

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Greeting.java:

package com.example.model;

public class Greeting {

    private String message;

    public Greeting() {
    }

    public Greeting(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

Creación de una aplicación de Spring Boot

Esta aplicación administra toda la lógica de negocios y tiene acceso al ecosistema completo de Spring Boot. Esta funcionalidad ofrece dos ventajas principales en comparación con una función estándar de Azure Functions:

  • No depende de las API de Azure Functions, por lo que se puede migrar fácilmente a otros sistemas. Por ejemplo, podría reutilizarse en una aplicación de Spring Boot normal.
  • Puede usar todas las anotaciones @Enable de Spring Boot para agregar nuevas características.

En la carpeta src/main/java/com/example, cree el siguiente archivo, que es una aplicación de Spring Boot normal:

DemoApplication.java:

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(DemoApplication.class, args);
    }
}

Ahora cree el siguiente archivo en la carpeta src/main/java/com/example/hello . Este código contiene un componente de Spring Boot que representa la función que queremos ejecutar:

Hello.java:

package com.example.hello;

import com.example.model.*;
import org.springframework.stereotype.Component;
import java.util.function.Function;

@Component
public class Hello implements Function<User, Greeting> {

    @Override
    public Greeting apply(User user) {
        return new Greeting("Hello, " + user.getName() + "!\n");
    }
}

Nota:

La función Hello es bastante específica:

  • Es una java.util.function.Function. Contiene la lógica de negocios y usa una API de Java estándar para transformar un objeto en otro.
  • Como tiene la anotación @Component, es un bean de Spring y, de manera predeterminada, su nombre es el de la clase pero empezando con minúscula: hello. Seguir esta convención de nomenclatura es importante si desea crear otras funciones en la aplicación. El nombre debe coincidir con el nombre en Azure Functions que crearemos en la sección siguiente.

Creación de la función de Azure

Para beneficiarse de la API completa de Azure Functions, ahora codificamos una función de Azure que delega su ejecución en la función de Spring Cloud creada en el paso anterior.

En la carpeta src/main/java/com/example/hello , cree el siguiente archivo de clase de función de Azure:

HelloHandler.java:

package com.example.hello;

import com.microsoft.azure.functions.*;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;
import com.example.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Optional;

@Component
public class HelloHandler {

    @Autowired
    private Hello hello;

    @FunctionName("hello")
    public HttpResponseMessage execute(
        @HttpTrigger(name = "request", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<User>> request, ExecutionContext context) {
        User user = request.getBody()
                           .filter(u -> u.getName() != null)
                           .orElseGet(() -> new User(request.getQueryParameters().getOrDefault("name", "world")));
        context.getLogger().info("Greeting user name: " + user.getName());
        return request.createResponseBuilder(HttpStatus.OK)
                      .body(hello.apply(user))
                      .header("Content-Type", "application/json")
                      .build();
    }
}

Esta clase de Java es una función de Azure, con las siguientes características interesantes:

  • La clase tiene la @Component anotación, por lo que es un Bean de Spring.
  • El nombre de la función, tal como se define en la anotación @FunctionName("hello"), es hello.
  • La clase implementa una función real de Azure, por lo que puede usar la API completa de Azure Functions aquí.

Adición de pruebas unitarias

Este paso es opcional, pero se recomienda validar que la aplicación funciona correctamente.

Cree la carpetasrc/test/java/com/example y agregue las siguientes pruebas de JUnit:

HelloTest.java:

package com.example;

import com.example.hello.Hello;
import com.example.model.Greeting;
import com.example.model.User;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class HelloTest {

    @Test
    public void test() {
        Greeting result = new Hello().apply(new User("foo"));
        assertThat(result.getMessage()).isEqualTo("Hello, foo!\n");
    }
}

Ahora puede probar la función de Azure con Maven:

mvn clean test

Ejecución de la función de forma local

Antes de implementar la aplicación en Azure Functions, vamos a probarla de forma local.

Primero debe empaquetar la aplicación en un archivo Jar:

mvn package

Ahora que la aplicación está empaquetada, puede ejecutarla mediante con el complemento azure-functions de Maven:

mvn azure-functions:run

La función de Azure estará disponible en el host local, a través del puerto 7071. Para probar la función, envíe una solicitud POST con un objeto User en formato JSON. Por ejemplo, con cURL:

curl -X POST http://localhost:7071/api/hello -d "{\"name\":\"Azure\"}"

La función responderá con un objeto Greeting, aún en formato JSON:

{
  "message": "Hello, Azure!\n"
}

Esta es una captura de pantalla de la solicitud cURL en la parte superior de la pantalla y la función local de Azure en la parte inferior:

Azure Function running locally

Depuración local de la función

En las secciones siguientes, se explica cómo depurar la función.

Depuración mediante Intellij IDEA

Abra el proyecto en Intellij IDEA y, a continuación, cree la configuración de ejecución Depuración remota de JVM para adjuntarla. Para más información, consulte Tutorial: Depuración remota.

Create a Remote JVM Debug run configuration

Ejecute la aplicación con el comando siguiente:

mvn azure-functions:run -DenableDebug

Cuando se inicia la aplicación, verá la salida siguiente:

Worker process started and initialized.
Listening for transport dt_socket at address: 5005

Inicie la depuración del proyecto en IntelliJ IDEA. Verá la salida siguiente:

Connected to the target VM, address: 'localhost:5005', transport: 'socket'

Marque los puntos de interrupción que desea depurar. Intellij IDEA entrará en modo de depuración después de enviar una solicitud.

Depuración mediante Visual Studio Code

Abra el proyecto en Visual Studio Code y, a continuación, configure el contenido siguiente en el archivo launch.json:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "java",
            "name": "Attach to Remote Program",
            "request": "attach",
            "hostName": "127.0.0.1",
            "port": 5005
        }
    ]
}

Ejecute la aplicación con el comando siguiente:

mvn azure-functions:run -DenableDebug

Cuando se inicia la aplicación, verá la salida siguiente:

Worker process started and initialized.
Listening for transport dt_socket at address: 5005

Inicie la depuración del proyecto en Visual Studio Code y, a continuación, marque los puntos de interrupción que desea depurar. Visual Studio Code entrará en modo de depuración después de enviar una solicitud. Para más información, consulte Ejecución y depuración en Java.

Implementación de la función en Azure Functions

Ahora, va a publicar la función de Azure en producción. Recuerde que las <functionAppName>propiedades , <functionAppRegion>y <functionResourceGroup> que ha definido en el archivo pom.xml se usan para configurar la función.

Nota:

El complemento de Maven se debe autenticar en Azure. Si ha instalado la CLI de Azure, use az login antes de continuar. Para más opciones de autenticación, consulte Autenticación en el repositorio azure-maven-plugins.

Ejecute Maven para implementar la función automáticamente:

mvn azure-functions:deploy

Ahora, vaya a Azure Portal para buscar el Function App que se ha creado.

Seleccione la función:

  • En la información general de la función, anote la dirección URL de la función.
  • Para comprobar la función en ejecución, seleccione Streaming de registro en el menú de navegación.

Ahora, igual que hizo en la sección anterior, use cURL para acceder a la función en ejecución, como se muestra en el ejemplo siguiente. Asegúrese de reemplazar your-function-name por el nombre real de la función.

curl https://your-function-name.azurewebsites.net/api/hello -d "{\"name\":\"Azure\"}"

Igual que en la sección anterior, la función responderá con un objeto Greeting, aún en formato JSON:

{
  "message": "Hello, Azure!\n"
}

Enhorabuena, ya tiene una función de Spring Cloud Function en ejecución en Azure Functions. Para obtener más información y ejemplos de funciones de Spring Cloud, consulte los siguientes recursos:

Pasos siguientes

Para más información acerca de Spring y Azure, vaya al centro de documentación de Azure.