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

1. Visão geral

O Spring Integration oferece um mecanismo de mensagens para trocar Messages por MessageChannels. Ele usa adaptadores de canal para se comunicar com sistemas externos.

Neste exercício, vamos criar dois apps que se comunicam usando os adaptadores de canal da Spring Integration fornecidos pelo Spring Cloud GCP. Esses adaptadores fazem com que a integração do Spring use 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.

Neste tutorial, usaremos o exemplo de código do guia de primeiros passos do Spring Boot.

O que você vai aprender

  • Como trocar mensagens entre apps com o Google Cloud Pub/Sub usando o Spring Integration 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 em 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 em 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 esta for a primeira vez que você inicia o Cloud Shell, uma tela intermediária vai aparecer com a descrição dele. Se isso acontecer, clique em Continuar.

9c92662c6a846a5c.png

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

9f0e51b578fecce5.png

Essa máquina virtual contém 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. Neste codelab, quase todo o trabalho pode ser feito com um navegador.

Depois de se conectar ao Cloud Shell, você vai ver que sua conta já está autenticada e que o projeto está 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 comando a seguir no Cloud Shell para confirmar se 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

Acesse 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 de criar o tópico, permaneça na página "Tópicos". Procure o tópico que você acabou de criar, pressione os três pontos verticais no fim da linha e clique em Nova assinatura.

975efa26e5054936.png

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

f7a91d9e1cb48009.png

4. Inicializar aplicativos Spring Boot

Depois que o Cloud Shell for iniciado, use a linha de comando para gerar dois novos aplicativos 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 app de envio de mensagens. Mude para o diretório do app de envio.

$ cd spring-integration-sender

Queremos que nosso app escreva mensagens em um canal. Depois que uma mensagem está no canal, ela é capturada pelo adaptador de canal de saída, que a converte de uma mensagem genérica do Spring para 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 na 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 elas vão depois de estarem lá?

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. Neste 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 no lugar, agora podemos conectar automaticamente um objeto PubsubOutboundGateway e usá-lo para escrever 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 está escutando 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 está escutando solicitações POST que contêm uma mensagem na porta 8080 e no endpoint /postMessage, mas vamos falar disso 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, mude para o diretório do app receptor:

$ cd spring-integration-receiver

No app anterior, a declaração do gateway de mensagens criou o canal de saída para nós. Como não usamos um gateway de mensagens para receber mensagens, precisamos declarar nosso próprio MessageChannel, que é 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 retransmiti-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 fica aguardando 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 um @ServiceActivator que é acionado quando novas mensagens chegam ao pubsubInputChannel. Nesse caso, vamos apenas registrar 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 enviadas ao app remetente serão registradas no app destinatário. 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 enviada.

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

Você configura dois apps Spring Boot que usam os adaptadores de canais da Spring Integration 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 canais de 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.