Mensagens com a integração do Spring e o Google Cloud Pub/Sub

A integração do Spring fornece a você um mecanismo de mensagens para trocar Messages por MessageChannels. Ele usa adaptadores de canal para se comunicar com sistemas externos.

Neste exercício, criaremos dois apps que se comunicam usando os adaptadores de canal do 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, usamos o código de exemplo do Guia de primeiros passos da Spring Boot.

O que você aprenderá

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

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ê 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

Configuração de ambiente personalizada

Se você ainda não tem uma Conta do Google (Gmail ou Google Apps), crie uma. Faça login no Console do Google Cloud Platform (console.cloud.google.com) e crie um novo projeto:

Captura de tela de 10/02/2016 12:45:26.png

Lembre-se do código do projeto, um nome exclusivo em todos os projetos do Google Cloud. O nome acima já foi escolhido e não servirá para você. Faremos referência a ele mais adiante neste codelab como PROJECT_ID.

Em seguida, você precisará ativar o faturamento no Console do Cloud para usar os recursos do Google Cloud.

A execução por meio deste codelab terá um custo baixo, mas poderá ser mais se você decidir usar mais recursos ou se deixá-los em execução. Consulte a seção "limpeza" no final deste documento.

Novos usuários do Google Cloud Platform estão qualificados para um teste sem custo financeiro de US$300.

Google Cloud Shell

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

Ativa o Google Cloud Shell

No Console do GCP, clique no ícone do Cloud Shell na barra de ferramentas localizada no canto superior direito:

Em seguida, clique em "Start Cloud Shell":

O provisionamento e a conexão ao ambiente levarão apenas alguns instantes para serem concluídos:

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. Praticamente todo o seu trabalho neste laboratório pode ser feito em um navegador ou no seu Google Chromebook.

Depois que você se conectar ao Cloud Shell, sua autenticação já terá sido feita, e o projeto estará definido com seu PROJECT_ID.

Execute o seguinte comando no Cloud Shell para confirmar se a conta está autenticada:

gcloud auth list

Resposta ao comando

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

Resposta ao comando

[core]
project = <PROJECT_ID>

Se o projeto não estiver configurado, faça a configuração usando este comando:

gcloud config set project <PROJECT_ID>

Resposta ao comando

Updated property [core/project].

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

Clique em Criar tópico.

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

Após a criação do tópico, permaneça na página de tópicos. Procure o tópico que você criou, pressione os três pontos verticais no final da linha e clique em Nova assinatura.

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

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=2.0.6.RELEASE \
  -d dependencies=web \
  -d baseDir=spring-integration-sender | tar -xzvf -
$ curl https://start.spring.io/starter.tgz \
  -d bootVersion=2.0.6.RELEASE \
  -d baseDir=spring-integration-receiver | tar -xzvf -

Agora, vamos criar o app de envio de mensagens. Mude para o diretório dele.

$ cd spring-integration-sender

Queremos que nosso app grave mensagens em um canal. Depois que uma mensagem é encontrada no canal, ela é recebida pelo adaptador do canal de saída, que a converte em uma mensagem Spring genérica 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 de integração do Spring (em inglês). 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 (em inglês)

...
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 ficam no canal?

Precisamos de um adaptador de canais 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 (em inglês)

...
import org.springframework.cloud.gcp.pubsub.core.PubSubTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.cloud.gcp.pubsub.integration.outbound.PubSubMessageHandler;
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 canais de saída, PubSubMessageHandler, para publicar a mensagem no tópico exampleTopic do Google Cloud Pub/Sub.

Com o adaptador de canais ativado, podemos conectar um objeto PubsubOutboundGateway automaticamente e usá-lo para escrever uma mensagem em um canal.

src/main/java/com/example/demo/DemoApplication.java (em inglês)

...
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 POST HTTP, 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 (em inglês)

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

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

Para que o app seja executado, basta adicionar as dependências necessárias.

pom.xml (link em inglês)

<project>
  ...
  <!-- Add Spring Cloud GCP Dependency BOM -->
  <dependencyManagement>
        <dependencies>
                <dependency>
                        <groupId>org.springframework.cloud</groupId>
                        <artifactId>spring-cloud-gcp-dependencies</artifactId>
                        <version>1.0.0.RELEASE</version>
                        <type>pom</type>
                        <scope>import</scope>
                </dependency>
        </dependencies>
  </dependencyManagement>

  <dependencies>
        ...
        <!-- Add Pub/Sub Starter -->
        <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-gcp-starter-pubsub</artifactId>
        </dependency>

        <!-- Add Spring Integration -->
        <dependency>
                <groupId>org.springframework.integration</groupId>
                <artifactId>spring-integration-core</artifactId>
        </dependency>

  </dependencies>

</project>

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á ouvindo solicitações POST que contêm uma mensagem na porta 8080 e no endpoint /postMessage, mas falaremos sobre isso mais tarde.

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

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

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 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 onde as mensagens recebidas chegarão.

src/main/java/com/example/demo/DemoApplication.java (em inglês)

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

O adaptador de canal de entrada precisará receber mensagens do Google Cloud Pub/Sub e redirecioná-las para pubsubInputChannel.

src/main/java/com/example/demo/DemoApplication.java (em inglês)

...
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.gcp.pubsub.core.PubSubTemplate;
import org.springframework.cloud.gcp.pubsub.integration.inbound.PubSubInboundChannelAdapter;

@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á-los com um @ServiceActivator que é acionado quando novas mensagens chegam a pubsubInputChannel.

src/main/java/com/example/demo/DemoApplication.java (em inglês)

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

Nesse caso, vamos registrar o payload da mensagem.

Precisamos adicionar as dependências necessárias.

pom.xml (link em inglês)

<project>
  ...
  <!-- Add Spring Cloud GCP Dependency BOM -->
  <dependencyManagement>
        <dependencies>
                <dependency>
                        <groupId>org.springframework.cloud</groupId>
                        <artifactId>spring-cloud-gcp-dependencies</artifactId>
                        <version>1.0.0.RELEASE</version>
                        <type>pom</type>
                        <scope>import</scope>
                </dependency>
        </dependencies>
  </dependencyManagement>

  <dependencies>
        ...
        <!-- Add Pub/Sub Starter -->
        <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-gcp-starter-pubsub</artifactId>
        </dependency>

        <!-- Add Spring Integration -->
        <dependency>
                <groupId>org.springframework.integration</groupId>
                <artifactId>spring-integration-core</artifactId>
        </dependency>

  </dependencies>

</project>

Execute o app receptor.

$ ./mvnw spring-boot:run

Agora todas as mensagens que você enviar para o app remetente serão registradas no app receptor. Para testar, abra uma nova sessão do Cloud Shell e faça uma solicitação HTTP POST ao 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!

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

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

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

Você aprendeu a usar os adaptadores de canal do Spring Integration 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.