Envía mensajes con Spring Integration y Google Cloud Pub/Sub

1. Descripción general

Spring Integration te proporciona un mecanismo de mensajería para intercambiar Messages a través de MessageChannels. Usa adaptadores de canal para comunicarse con sistemas externos.

En este ejercicio, crearemos dos aplicaciones que se comuniquen mediante los adaptadores de canales de Spring Integration proporcionados por Spring Cloud GCP. Estos adaptadores hacen que Spring Integration use Google Cloud Pub/Sub como backend de intercambio de mensajes.

Asimismo, aprenderá a utilizar Cloud Shell y el comando gcloud del SDK de Cloud.

En este instructivo, se usa el código de muestra de la guía de introducción a Spring Boot.

Qué aprenderás

  • Cómo intercambiar mensajes entre apps con Google Cloud Pub/Sub mediante Spring Integration y Spring Cloud GCP

Requisitos

  • Un proyecto de Google Cloud Platform
  • Un navegador como Chrome o Firefox
  • Se recomienda estar familiarizado con editores de texto estándares de Linux, como Vim, Emacs o Nano.

¿Cómo usarás este instructivo?

Ler Leer y completar los ejercicios

¿Cómo calificarías tu experiencia con la creación de aplicaciones web HTML/CSS?

Principiante Intermedio Avanzado

¿Cómo calificarías tu experiencia en el uso de los servicios de Google Cloud Platform?

Principiante Intermedio Avanzado .
.

2. Configuración y requisitos

Configuración del entorno de autoaprendizaje

  1. Accede a Google Cloud Console y crea un proyecto nuevo o reutiliza uno existente. Si aún no tienes una cuenta de Gmail o de Google Workspace, debes crear una.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • El Nombre del proyecto es el nombre visible de los participantes de este proyecto. Es una cadena de caracteres que no se utiliza en las APIs de Google. Puedes actualizarla cuando quieras.
  • El ID del proyecto es único en todos los proyectos de Google Cloud y es inmutable (no se puede cambiar después de configurarlo). La consola de Cloud genera automáticamente una cadena única. Por lo general, no importa cuál sea. En la mayoría de los codelabs, deberás hacer referencia al ID de tu proyecto (suele identificarse como PROJECT_ID). Si no te gusta el ID que se generó, podrías generar otro aleatorio. También puedes probar uno propio y ver si está disponible. No se puede cambiar después de este paso y se usa el mismo durante todo el proyecto.
  • Recuerda que hay un tercer valor, un número de proyecto, que usan algunas APIs. Obtén más información sobre estos tres valores en la documentación.
  1. A continuación, deberás habilitar la facturación en la consola de Cloud para usar las APIs o los recursos de Cloud. Ejecutar este codelab no costará mucho, tal vez nada. Para cerrar recursos y evitar que se generen cobros más allá de este instructivo, puedes borrar los recursos que creaste o borrar el proyecto. Los usuarios nuevos de Google Cloud son aptos para participar en el programa Prueba gratuita de $300.

Google Cloud Shell

Si bien Google Cloud se puede operar de manera remota desde tu laptop, en este codelab usaremos Google Cloud Shell, un entorno de línea de comandos que se ejecuta en la nube.

Activar Cloud Shell

  1. En la consola de Cloud, haz clic en Activar Cloud Shell853e55310c205094.png.

55efc1aaa7a4d3ad.png

Si es la primera vez que inicias Cloud Shell, verás una pantalla intermedia que describe en qué consiste. Si apareció una pantalla intermedia, haz clic en Continuar.

9c92662c6a846a5c.png

El aprovisionamiento y la conexión a Cloud Shell solo tomará unos minutos.

9f0e51b578fecce5.png

Esta máquina virtual está cargada con todas las herramientas de desarrollo necesarias. Ofrece un directorio principal persistente de 5 GB y se ejecuta en Google Cloud, lo que mejora considerablemente el rendimiento de la red y la autenticación. Gran parte de tu trabajo en este codelab, si no todo, se puede hacer con un navegador.

Una vez que te conectes a Cloud Shell, deberías ver que estás autenticado y que el proyecto está configurado con tu ID del proyecto.

  1. En Cloud Shell, ejecuta el siguiente comando para confirmar que tienes la autenticación:
gcloud auth list

Resultado del comando

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Ejecuta el siguiente comando en Cloud Shell para confirmar que el comando de gcloud conoce tu proyecto:
gcloud config list project

Resultado del comando

[core]
project = <PROJECT_ID>

De lo contrario, puedes configurarlo con el siguiente comando:

gcloud config set project <PROJECT_ID>

Resultado del comando

Updated property [core/project].

3. Aprovisiona recursos de Pub/Sub

Navega a la página de temas de Google Cloud Pub/Sub.

Haz clic en Crear tema.

4c938409dc7169a6.png

Escribe exampleTopic como el nombre del tema y, luego, haz clic en Crear.

e2daeec91537f672.png

Después de crear el tema, permanece en la página Temas. Busca el tema que acabas de crear, presiona los tres puntos verticales al final de la línea y haz clic en Nueva suscripción.

975efa26e5054936.png

Escribe exampleSubscription en el cuadro de texto del nombre de la suscripción y haz clic en Crear.

f7a91d9e1cb48009.png

4. Inicializa aplicaciones de Spring Boot

Después de que se inicie Cloud Shell, puedes usar la línea de comandos para generar dos aplicaciones de Spring Boot nuevas con Spring Initializr:

$ curl https://start.spring.io/starter.tgz \
  -d bootVersion=3.0.5 \
  -d dependencies=web,integration,cloud-gcp-pubsub \
  -d type=maven-project \
  -d baseDir=spring-integration-sender | tar -xzvf -

$ curl https://start.spring.io/starter.tgz \
  -d bootVersion=3.0.5 \
  -d dependencies=web,integration,cloud-gcp-pubsub \
  -d type=maven-project \
  -d baseDir=spring-integration-receiver | tar -xzvf -

5. Crea una aplicación para enviar mensajes

Ahora creemos nuestra app para enviar mensajes. Cambia al directorio de la app emisora.

$ cd spring-integration-sender

Queremos que nuestra app escriba mensajes en un canal. Después de que un mensaje está en el canal, el adaptador del canal saliente lo recoge, lo que lo convierte de un mensaje Spring genérico en un mensaje de Google Cloud Pub/Sub y lo publica en un tema de Google Cloud Pub/Sub.

Para que nuestra app escriba en un canal, podemos usar una puerta de enlace de mensajería de Spring Integration. Con un editor de texto de vim, emacs o nano, declara una interfaz PubsubOutboundGateway dentro de la clase DemoApplication.

src/main/java/com/example/demo/DemoApplication.java

...
import org.springframework.integration.annotation.MessagingGateway;

@SpringBootApplication
public class DemoApplication {

  ...

  @MessagingGateway(defaultRequestChannel = "pubsubOutputChannel")
  public interface PubsubOutboundGateway {
    void sendToPubsub(String text);
  }
}

Ahora tenemos un mecanismo para enviar mensajes a un canal, pero ¿adónde se dirigen esos mensajes después de estar en el canal?

Necesitamos un adaptador de canal saliente para consumir mensajes nuevos en el canal y publicarlos en un tema de Google Cloud Pub/Sub.

src/main/java/com/example/demo/DemoApplication.java

...
import com.google.cloud.spring.pubsub.core.PubSubTemplate;
import com.google.cloud.spring.pubsub.integration.outbound.PubSubMessageHandler;

import org.springframework.context.annotation.Bean;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.messaging.MessageHandler;

@SpringBootApplication
public class DemoApplication {

  ...

  @Bean
  @ServiceActivator(inputChannel = "pubsubOutputChannel")
  public MessageHandler messageSender(PubSubTemplate pubsubTemplate) {
    return new PubSubMessageHandler(pubsubTemplate, "exampleTopic");
  }
}

La anotación @ServiceActivator hace que este MessageHandler se aplique a todos los mensajes nuevos de inputChannel. En este caso, llamamos a nuestro adaptador de canal saliente, PubSubMessageHandler, para publicar el mensaje en el tema exampleTopic de Google Cloud Pub/Sub.

Con el adaptador de canales en su lugar, ahora podemos conectar automáticamente un objeto PubsubOutboundGateway y usarlo para escribir un mensaje a un canal.

src/main/java/com/example/demo/DemoApplication.java

...
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.view.RedirectView;

@SpringBootApplication
public class DemoApplication {

  ...

  @Autowired
  private PubsubOutboundGateway messagingGateway;

  @PostMapping("/postMessage")
  public RedirectView postMessage(@RequestParam("message") String message) {
    this.messagingGateway.sendToPubsub(message);
    return new RedirectView("/");
  }
}

Debido a la anotación @PostMapping, ahora tenemos un extremo que escucha las solicitudes HTTP POST, pero no sin agregar una anotación @RestController a la clase DemoApplication para marcarla como controlador de REST.

src/main/java/com/example/demo/DemoApplication.java

import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class DemoApplication {
  ...
}

Asegúrate de que JAVA_HOME esté configurado en la versión correcta.

export JAVA_HOME=/usr/lib/jvm/java-1.17.0-openjdk-amd64

Ejecuta la app emisora.

# Set the Project ID in environmental variable
$ export GOOGLE_CLOUD_PROJECT=`gcloud config list --format 'value(core.project)'`

$ ./mvnw spring-boot:run

La app está escuchando solicitudes POST que contienen un mensaje en el puerto 8080 y el extremo /postMessage, pero abordaremos esto más adelante.

6. Crea una aplicación para recibir mensajes

Acabamos de crear una aplicación que envía mensajes a través de Google Cloud Pub/Sub. Ahora, crearemos otra app que reciba esos mensajes y los procese.

Haz clic en + para abrir una nueva sesión de Cloud Shell.

9799bee5fea95aa6.png

Luego, en la nueva sesión de Cloud Shell, cambia los directorios al directorio de la app receptora:

$ cd spring-integration-receiver

En la app anterior, la declaración de la puerta de enlace de mensajería creó el canal de salida por nosotros. Dado que no usamos una puerta de enlace de mensajería para recibir mensajes, debemos declarar nuestra propia MessageChannel a la que llegarán los mensajes entrantes.

src/main/java/com/example/demo/DemoApplication.java

...
import org.springframework.context.annotation.Bean;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.messaging.MessageChannel;

@SpringBootApplication
public class DemoApplication {

  ...

  @Bean
  public MessageChannel pubsubInputChannel() {
    return new DirectChannel();
  }
}

Necesitaremos el adaptador de canal entrante para recibir mensajes de Google Cloud Pub/Sub y retransmitirlos a pubsubInputChannel.

src/main/java/com/example/demo/DemoApplication.java

...
import com.google.cloud.spring.pubsub.core.PubSubTemplate;
import com.google.cloud.spring.pubsub.integration.inbound.PubSubInboundChannelAdapter;

import org.springframework.beans.factory.annotation.Qualifier;

@SpringBootApplication
public class DemoApplication {

  ...

  @Bean
  public PubSubInboundChannelAdapter messageChannelAdapter(
      @Qualifier("pubsubInputChannel") MessageChannel inputChannel,
      PubSubTemplate pubSubTemplate) {
    PubSubInboundChannelAdapter adapter =
        new PubSubInboundChannelAdapter(pubSubTemplate, "exampleSubscription");
    adapter.setOutputChannel(inputChannel);

    return adapter;
  }
}

Este adaptador se vincula a sí mismo a pubsubInputChannel y escucha mensajes nuevos de la suscripción exampleSubscription de Google Cloud Pub/Sub.

Tenemos un canal en el que se publican los mensajes entrantes, pero ¿qué se debe hacer con ellos?

Vamos a procesarlos con un @ServiceActivator que se activa cuando llegan mensajes nuevos a pubsubInputChannel. En este caso, solo registraremos la carga útil del mensaje.

src/main/java/com/example/demo/DemoApplication.java

...
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.integration.annotation.ServiceActivator;

@SpringBootApplication
public class DemoApplication {

  ...

  private static final Log LOGGER = LogFactory.getLog(DemoApplication.class);

  @ServiceActivator(inputChannel = "pubsubInputChannel")
  public void messageReceiver(String payload) {
    LOGGER.info("Message arrived! Payload: " + payload);
  }
}

Asegúrate de que JAVA_HOME esté configurado en la versión correcta.

export JAVA_HOME=/usr/lib/jvm/java-1.17.0-openjdk-amd64

Ejecuta la app receptora.

$ ./mvnw spring-boot:run -Dspring-boot.run.jvmArguments="-Dserver.port=8081"

Ahora todos los mensajes que envíes a la app del remitente se registrarán en la app receptora. Para probarlo, abre una nueva sesión de Cloud Shell y realiza una solicitud HTTP POST a la app emisora.

$ curl --data "message=Hello world!" localhost:8080/postMessage

Luego, verifica que la app receptora haya registrado el mensaje que enviaste.

INFO: Message arrived! Payload: Hello world!

7. Limpieza

Borra la suscripción y el tema que se crearon como parte de este ejercicio.

$ gcloud pubsub subscriptions delete exampleSubscription
$ gcloud pubsub topics delete exampleTopic

8. Resumen

Configura dos apps de Spring Boot que usan Spring Integration Channel Adapters para Google Cloud Pub/Sub. Intercambian mensajes entre ellos sin interactuar nunca con la API de Google Cloud Pub/Sub.

9. ¡Felicitaciones!

Aprendiste a usar los adaptadores de canales de integración de Spring para Google Cloud Pub/Sub.

Más información

Licencia

Este trabajo cuenta con una licencia Atribución 2.0 Genérica de Creative Commons.