Como enviar mensagens com o Spring Integration e o Google Cloud Pub/Sub

1. Visão geral

A Spring Integration oferece um mecanismo de mensagens para trocar Messages pelo MessageChannels. Ele usa adaptadores de canais para se comunicar com sistemas externos.

Neste exercício, criaremos dois aplicativos que se comunicam usando os adaptadores de canal do Spring Integration fornecidos pelo Spring Cloud GCP. Com esses adaptadores, o Spring Integration usa o Google Cloud Pub/Sub como back-end de troca de mensagens.

Depois, também verá como usar o Cloud Shell e o comando gcloud do SDK do Cloud.

Este tutorial usa o exemplo de código do Guia explicativo do Spring Boot.

O que você vai aprender

  • Como trocar mensagens entre aplicativos com o Google Cloud Pub/Sub usando a integração do Spring e o Spring Cloud GCP

O que é necessário

  • Um projeto do Google Cloud Platform
  • Um navegador, como o Chrome ou o Firefox
  • Conhecer os editores de texto padrão do Linux, como vim, emacs ou nano

Como você vai usar este tutorial?

Apenas leitura Leitura e exercícios

Como você classificaria sua experiência com a criação de apps da Web HTML/CSS?

Iniciante Intermediário Proficiente

Como você classificaria sua experiência com o uso dos serviços do Google Cloud Platform?

Iniciante Intermediário Proficiente

2. Configuração e requisitos

Configuração de ambiente autoguiada

  1. Faça login no Console do Google Cloud e crie um novo projeto ou reutilize um existente. Crie uma conta do Gmail ou do Google Workspace, se ainda não tiver uma.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • O Nome do projeto é o nome de exibição para os participantes do projeto. É uma string de caracteres não usada pelas APIs do Google e pode ser atualizada quando você quiser.
  • O ID do projeto precisa ser exclusivo em todos os projetos do Google Cloud e não pode ser mudado após a definição. O console do Cloud gera automaticamente uma string exclusiva. Em geral, não importa o que seja. Na maioria dos codelabs, é necessário fazer referência ao ID do projeto, normalmente identificado como PROJECT_ID. Se você não gostar do ID gerado, crie outro aleatório. Se preferir, teste o seu e confira se ele está disponível. Ele não pode ser mudado após essa etapa e permanece durante o projeto.
  • Para sua informação, há um terceiro valor, um Número do projeto, que algumas APIs usam. Saiba mais sobre esses três valores na documentação.
  1. Em seguida, ative o faturamento no console do Cloud para usar os recursos/APIs do Cloud. A execução deste codelab não vai ser muito cara, se tiver algum custo. Para encerrar os recursos e evitar cobranças além deste tutorial, exclua os recursos criados ou exclua o projeto. Novos usuários do Google Cloud estão qualificados para o programa de US$ 300 de avaliação sem custos.

Google Cloud Shell

Embora o Google Cloud possa ser operado remotamente do seu laptop, neste codelab vamos usar o Google Cloud Shell, um ambiente de linha de comando executado no Cloud.

Ativar o Cloud Shell

  1. No Console do Cloud, clique em Ativar o Cloud Shell853e55310c205094.png.

55efc1aaa7a4d3ad.png

Se você estiver iniciando o Cloud Shell pela primeira vez, verá uma tela intermediária com a descrição dele. Se aparecer uma tela intermediária, clique em Continuar.

9c92662c6a846a5c.png

Leva apenas alguns instantes para provisionar e se conectar ao Cloud Shell.

9f0e51b578fecce5.png

Essa máquina virtual tem todas as ferramentas de desenvolvimento necessárias. Ela oferece um diretório principal persistente de 5 GB, além de ser executada no Google Cloud. Isso aprimora o desempenho e a autenticação da rede. Grande parte do trabalho neste codelab, se não todo, pode ser feito em um navegador.

Depois de se conectar ao Cloud Shell, você verá sua autenticação e o projeto estará configurado com o ID do seu projeto.

  1. Execute o seguinte comando no Cloud Shell para confirmar se a conta está autenticada:
gcloud auth list

Resposta ao comando

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

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Execute o seguinte comando no Cloud Shell para confirmar que o comando gcloud sabe sobre seu projeto:
gcloud config list project

Resposta ao comando

[core]
project = <PROJECT_ID>

Se o projeto não estiver configurado, configure-o usando este comando:

gcloud config set project <PROJECT_ID>

Resposta ao comando

Updated property [core/project].

3. Provisionar recursos do Pub/Sub

Navegue até a página de tópicos do Google Cloud Pub/Sub.

Clique em Criar tópico.

4c938409dc7169a6.png

Digite exampleTopic como o nome do tópico e clique em Criar.

e2daeec91537f672.png

Depois que o tema for criado, permaneça na página "Tópicos". Procure o tópico que você acabou de criar, pressione os três pontos verticais no final da linha e clique em Nova assinatura.

975efa26e5054936.png

Digite exampleSubscription na caixa de texto do nome da assinatura e clique em Criar.

f7a91d9e1cb48009.png

4. Inicializar aplicativos Spring Boot

Depois que o Cloud Shell for iniciado, você poderá usar a linha de comando para gerar dois novos aplicativos do Spring Boot com o 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. Criar um aplicativo para enviar mensagens

Agora, vamos criar nosso aplicativo de envio de mensagens. Mude para o diretório do app de envio.

$ cd spring-integration-sender

Queremos que nosso app escreva mensagens para um canal. Depois que uma mensagem chega ao canal, ela é recebida pelo adaptador de canal de saída, que a converte de uma mensagem genérica do Spring em uma mensagem do Google Cloud Pub/Sub e a publica em um tópico do Google Cloud Pub/Sub.

Para que nosso app grave em um canal, podemos usar um gateway de mensagens da Spring Integration. Usando um editor de texto de vim, emacs ou nano, declare uma interface PubsubOutboundGateway dentro da classe 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);
  }
}

Agora temos um mecanismo para enviar mensagens a um canal, mas para onde essas mensagens vão depois que estão no canal?

Precisamos de um adaptador de canal de saída para consumir novas mensagens no canal e publicá-las em um tópico do 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");
  }
}

A anotação @ServiceActivator faz com que esse MessageHandler seja aplicado a todas as novas mensagens em inputChannel. Nesse caso, estamos chamando nosso adaptador de canal de saída, PubSubMessageHandler, para publicar a mensagem no tópico exampleTopic do Google Cloud Pub/Sub.

Com o adaptador de canal instalado, agora podemos conectar automaticamente um objeto PubsubOutboundGateway e usá-lo para gravar uma mensagem em um 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("/");
  }
}

Devido à anotação @PostMapping, agora temos um endpoint que detecta solicitações HTTP POST, mas não sem adicionar também uma anotação @RestController à classe DemoApplication para marcá-la como um controlador REST.

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

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

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

Verifique se JAVA_HOME está definido com a versão correta.

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

Execute o app remetente.

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

$ ./mvnw spring-boot:run

O app ouve solicitações POST que contêm uma mensagem na porta 8080 e no endpoint /postMessage, mas vamos abordar isso mais tarde.

6. Criar um aplicativo para receber mensagens

Acabamos de criar um app que envia mensagens pelo Google Cloud Pub/Sub. Agora, vamos criar outro app que recebe e processa essas mensagens.

Clique em + para abrir uma nova sessão do Cloud Shell.

9799bee5fea95aa6.png

Em seguida, na nova sessão do Cloud Shell, altere os diretórios para o diretório do app receptor:

$ cd spring-integration-receiver

No app anterior, a declaração do gateway de mensagens criava o canal de saída. Como não usamos um gateway para receber mensagens, precisamos declarar nosso próprio MessageChannel, onde as mensagens recebidas vão chegar.

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();
  }
}

Vamos precisar do adaptador de canal de entrada para receber mensagens do Google Cloud Pub/Sub e redirecioná-las para 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;
  }
}

Esse adaptador se vincula ao pubsubInputChannel e detecta novas mensagens da assinatura exampleSubscription do Google Cloud Pub/Sub.

Temos um canal em que as mensagens recebidas são postadas, mas o que fazer com elas?

Vamos processá-las com uma @ServiceActivator que é acionada quando novas mensagens chegam a pubsubInputChannel. Neste caso, registraremos apenas o payload da mensagem.

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);
  }
}

Verifique se JAVA_HOME está definido com a versão correta.

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

Execute o app receptor.

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

Agora, todas as mensagens que você enviar ao app remetente serão registradas no app receptor. Para testar isso, abra uma nova sessão do Cloud Shell e faça uma solicitação HTTP POST para o app remetente.

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

Em seguida, verifique se o app receptor registrou a mensagem que você enviou.

INFO: Message arrived! Payload: Hello world!

7. Limpeza

Exclua a assinatura e o tópico criados como parte deste exercício.

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

8. Resumo

Configure dois aplicativos do Spring Boot que usam adaptadores de canal da integração do Spring para o Google Cloud Pub/Sub. Eles trocam mensagens entre si sem nunca interagir com a API Google Cloud Pub/Sub.

9. Parabéns!

Você aprendeu a usar os adaptadores de canal da integração do Spring para o Google Cloud Pub/Sub.

Saiba mais

Licença

Este conteúdo está sob a licença Atribuição 2.0 Genérica da Creative Commons.