1. Introdução
O Spring Framework 5.0 adicionou suporte dedicado ao Kotlin, facilitando o uso do Spring para desenvolvedores Kotlin. Consequentemente, essas mudanças fizeram com que as integrações do Google Cloud fornecidas pelo Spring Cloud GCP (link em inglês) também funcionassem perfeitamente no Kotlin. Neste codelab, você verá como é fácil começar a usar os serviços do Google Cloud nos seus aplicativos Kotlin.
Este codelab mostra como configurar um aplicativo de registro simples em Kotlin que demonstra o uso de serviços do GCP, como o Cloud Pub/Sub e o Cloud SQL.
O que você vai criar
Neste codelab, você vai configurar um aplicativo Kotlin Spring Boot que aceita informações do responsável pelo registro, publica essas informações em um tópico do Cloud Pub/Sub e as mantém em um banco de dados do Cloud MySQL.
O que você vai aprender
Como fazer a integração com os serviços do Google Cloud no seu aplicativo Kotlin Spring.
O que é necessário
- Um projeto do Google Cloud Platform
- Um navegador, como o Chrome ou o Firefox
Como você vai usar este tutorial?
Como você classificaria sua experiência com a criação de apps da Web HTML/CSS?
Como você classificaria sua experiência com o uso dos serviços do Google Cloud Platform?
2. Configuração e requisitos
Configuração de ambiente autoguiada
- Faça login no Console do Cloud e crie um novo projeto ou reutilize um existente. Crie uma se você ainda não tiver uma conta do Gmail ou do G Suite.
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, será necessário ativar o faturamento no Console do Cloud para usar os recursos do Google Cloud.
A execução deste codelab não será muito cara, se for o caso. Siga todas as instruções na seção "Limpeza", que orienta você sobre como encerrar recursos para não incorrer em cobranças além deste tutorial. 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
- No Console do Cloud, clique em Ativar o Cloud Shell.
Se você nunca tiver iniciado o Cloud Shell, verá uma tela intermediária (abaixo da dobra) com a descrição do que ele é. Se esse for o caso, clique em Continuar e você não o verá novamente. Esta é uma tela única:
Leva apenas alguns instantes para provisionar e se conectar ao Cloud Shell.
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 codelab pode ser feito em um navegador ou no seu Chromebook.
Depois de se conectar ao Cloud Shell, você já estará autenticado e o projeto já estará configurado com seu ID do projeto.
- Execute o seguinte comando no Cloud Shell para confirmar que você está autenticado:
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`
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
Primeiro, é preciso configurar um tópico e uma assinatura do Cloud Pub/Sub. Neste aplicativo, publicaremos informações de registro em um tópico do Pub/Sub. Depois, as informações são lidas nesse tópico e mantidas em um banco de dados.
Neste tutorial, usaremos o Cloud Shell para provisionar nossos recursos. Também é possível configurar recursos do Pub/Sub na seção do Cloud Pub/Sub no console do Google Cloud.
No terminal do Cloud Shell, primeiro ative a API Pub/Sub.
$ gcloud services enable pubsub.googleapis.com
Em seguida, vamos criar um tópico do Pub/Sub chamado registrations
para este aplicativo. As informações de registro enviadas por meio do aplicativo serão publicadas neste tópico.
$ gcloud pubsub topics create registrations
Por fim, crie uma assinatura para o tópico. Uma assinatura do Pub/Sub permite receber mensagens de um tópico.
$ gcloud pubsub subscriptions create registrations-sub --topic=registrations
Você acabou de criar um tópico e uma assinatura do Cloud Pub/Sub para seu aplicativo.
4. Criar uma instância e um banco de dados do Cloud SQL (MySQL)
Para nosso exemplo de aplicativo, também precisamos configurar uma instância de banco de dados para manter as informações do responsável pelo registro. Esta etapa também vai depender do terminal do Cloud Shell para provisionar os recursos do Cloud SQL. Também é possível visualizar e configurar suas instâncias do Cloud SQL pelo Console do Google Cloud.
Primeiro, ative a API Cloud SQL Admin.
$ gcloud services enable sqladmin.googleapis.com
Em seguida, provisionaremos uma instância do Cloud SQL (MySQL). Esse comando pode levar algum tempo.
$ gcloud sql instances create codelab-instance --region=us-east1
Depois de criar a instância do Cloud SQL, crie um novo banco de dados chamado registrants
na instância.
$ gcloud sql databases create registrants --instance codelab-instance
Você concluiu a configuração da instância e do banco de dados do Cloud SQL para seu aplicativo.
5. Inicializar um aplicativo Spring Boot
Agora estamos prontos para começar a escrever o aplicativo. As próximas etapas continuarão usando o Cloud Shell descrito nas etapas de configuração.
Primeiro, vamos usar Initializr para gerar o código de scaffolding para o projeto. Na janela do Cloud Shell, execute:
$ cd ~
$ curl https://start.spring.io/starter.tgz \
-d language=kotlin \
-d bootVersion=2.4.0 \
-d dependencies=web,data-jpa,integration,cloud-gcp-pubsub,thymeleaf \
-d baseDir=registrations-codelab | tar -xzvf -
$ cd registrations-codelab
Esse comando gera uma configuração inicial do projeto Maven e um código de scaffolding para seu aplicativo no diretório registrations-codelab/
. As seções a seguir descrevem as edições de código necessárias para produzir um aplicativo funcional.
Editor de código do Cloud Shell
A maneira mais fácil de começar a modificar e visualizar código no ambiente do Cloud Shell é usar o editor de código do Cloud Shell integrado.
Depois de abrir uma instância do Cloud Shell, clique no ícone de lápis para abrir o editor de código. O editor permite que você modifique diretamente os arquivos de projeto produzidos pelo Initialzr.
6. Configuração do banco de dados
Primeiro, configure seu aplicativo para que ele possa se conectar ao banco de dados do Cloud MySQL que você definiu. As bibliotecas do Spring Cloud GCP oferecem uma inicialização do Cloud MySQL que fornece as dependências necessárias para se conectar a uma instância do Cloud MySQL.
Adicione a dependência spring-cloud-gcp-starter-sql-mysql
ao pom.xml do projeto:
registrations-codelab/pom.xml
... <dependencies> ... Other dependencies above ... <!-- Add the MySQL starter to the list of dependencies --> <dependency> <groupId>com.google.cloud</groupId> <artifactId>spring-cloud-gcp-starter-sql-mysql</artifactId> </dependency> </dependencies>
Além disso, você precisa modificar o arquivo de configuração application.properties
para descrever a configuração do seu banco de dados. Copie as seguintes propriedades no arquivo application.properties
.
Encontre o nome da conexão da instância no seu banco de dados:
$ gcloud sql instances describe codelab-instance \ --format 'value(connectionName)'
A saída será usada no arquivo application.properties
para configurar as informações de conexão.
src/main/resources/application.properties
# Modify this property using the output from the previous command line. spring.cloud.gcp.sql.instance-connection-name=INSTANCE_CONNECTION_NAME # Your database name spring.cloud.gcp.sql.database-name=registrants # So app starts despite "table already exists" errors. spring.datasource.continue-on-error=true # Enforces database initialization spring.datasource.initialization-mode=always # Cloud SQL (MySQL) only supports InnoDB, not MyISAM spring.jpa.database-platform=org.hibernate.dialect.MySQL55Dialect spring.jpa.hibernate.ddl-auto=create-drop # This is used if you want to connect to a different database instance # user other than root; not used in codelab. # spring.datasource.username=root # This is used to specify the password of the database user; # not used in codelab. # spring.datasource.password=password
A única propriedade que você precisa modificar é o nome da conexão da instância. Esse valor precisa ser formatado como um valor separado por dois-pontos com o formato: YOUR_GCP_PROJECT_ID:REGION:DATABASE_INSTANCE_NAME
.
7. Como criar o conteúdo estático
Primeiro, vamos criar o front-end do aplicativo. A inscrição deve ter um formulário que permita que alguém registre indivíduos e também uma visualização que mostre todos os inscritos bem-sucedidos.
Para a página inicial, crie um index.html
contendo o formulário de registro.
src/main/resources/static/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Registration Sample Application</title>
</head>
<body>
<h1>Registration</h1>
<div>
<nav>
<a href="/">Home</a><br>
<a href="/registrants">Registered People</a><br>
</nav>
<p>
This is a demo registration application which sends user information to a Pub/Sub topic and
persists it into a MySQL database.
</p>
<h2>Register Person</h2>
<div>
<form action="/registerPerson" method="post">
First Name: <input type="text" name="firstName" />
Last Name: <input type="text" name="lastName" />
Email: <input type="text" name="email" />
<input type="submit" value="Submit"/>
</form>
</div>
</div>
</body>
</html>
Em seguida, criaremos um modelo Thymeleaf chamado registrants.html
para exibir os usuários registrados. Thymeleaf é uma estrutura de modelos que usamos para construir e disponibilizar HTML criado dinamicamente. Você verá que o modelo se parece com HTML, exceto por conter alguns elementos de marcação adicionais para manipular conteúdo dinâmico. Este modelo aceita um único parâmetro chamado personsList
, que contém todos os responsáveis pelo registro que foram registrados pela inscrição.
src/main/resources/templates/registrants.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Registrants List</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<h1>Registrants List</h1>
<p>
This page displays all the people who were registered through the Pub/Sub topic.
All results are retrieved from the MySQL database.
</p>
<table border="1">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
</tr>
<tr th:each="person : ${personsList}">
<td>[[${person.firstName}]]</td>
<td>[[${person.lastName}]]</td>
<td>[[${person.email}]]</td>
</tr>
</table>
</body>
</html>
Neste ponto, é possível verificar se o conteúdo estático está sendo exibido.
Crie e execute o app usando o Maven:
$ ./mvnw spring-boot:run
Clique no botão "Visualizar" na janela do Cloud Shell e verifique se a página inicial está sendo renderizada. Nenhuma das funcionalidades na interface vai funcionar, porque um controlador da Web está ausente. Isso será adicionado na próxima etapa.
Depois de visualizar o aplicativo, pressione CTRL+C
para encerrá-lo.
8. Como enviar os registrados para um tópico do Pub/Sub
Nesta etapa, vamos implementar o recurso em que os inscritos enviados pelo formulário on-line serão publicados em um tópico do Cloud Pub/Sub.
Adicionar as classes de dados
Primeiro, vamos criar algumas classes de dados do Kotlin. essas entidades serão nossas entidades JPA e também atuarão como nossa representação intermediária dos responsáveis pelo registro enviados por meio do formulário.
No pacote de demonstração, adicione dois novos arquivos: uma classe Person
e um PersonRepository
do Spring Data. Essas duas classes nos permitirão armazenar e recuperar facilmente as entradas de registro do banco de dados MySQL usando o Spring Data JPA.
src/main/kotlin/com/example/demo/Person.kt
package com.example.demo
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.Id
@Entity
data class Person(
val firstName: String,
val lastName: String,
val email: String,
@Id @GeneratedValue
var id: Long? = 0)
src/main/kotlin/com/example/demo/PersonRepository.kt
package com.example.demo
import org.springframework.data.repository.CrudRepository
interface PersonRepository : CrudRepository<Person, Long>
Adicionar o controlador da Web
Em seguida, vamos criar uma classe de controlador que processa os inscritos do formulário e envia as informações para o tópico do Cloud Pub/Sub criado anteriormente. Esse controlador cria dois endpoints:
/registerPerson
: o endpoint POST em que as informações do responsável pelo registro são enviadas e depois enviadas para o tópico do Pub/Sub. Na funçãoregisterPerson(..)
, as informações do responsável pelo registro são enviadas para o tópico do Pub/Sub usandoPubSubTemplate
, uma classe de conveniência das integrações do Spring Cloud GCP Pub/Sub que minimiza o código boilerplate necessário para começar a interagir com o Cloud Pub/Sub./registrants
: mostra todos os responsáveis pelo registro registrados no banco de dados. Essas informações são recuperadas da instância do MySQL usando o repositório Spring Data que criamos na etapa anterior.
Crie a seguinte classe Controller no pacote de demonstração:
src/main/kotlin/com/example/demo/Controller.kt
package com.example.demo
import com.google.cloud.spring.pubsub.core.PubSubTemplate
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.servlet.ModelAndView
import org.springframework.web.servlet.view.RedirectView
@RestController
class Controller(val pubSubTemplate: PubSubTemplate, val personRepository: PersonRepository) {
// The Pub/Sub topic name created earlier.
val REGISTRATION_TOPIC = "registrations"
@PostMapping("/registerPerson")
fun registerPerson(
@RequestParam("firstName") firstName: String,
@RequestParam("lastName") lastName: String,
@RequestParam("email") email: String): RedirectView {
pubSubTemplate.publish(
REGISTRATION_TOPIC,
Person(firstName, lastName, email))
return RedirectView("/")
}
@GetMapping("/registrants")
fun getRegistrants(): ModelAndView {
val personsList = personRepository.findAll().toList()
return ModelAndView("registrants", mapOf("personsList" to personsList))
}
}
O controlador lê as informações do responsável pelo registro enviadas pelo formulário da Web e publica as informações no tópico do Pub/Sub.
Como adicionar o JSON Object Mapper Bean
Talvez você tenha notado no Controlador que publicamos um objeto Person
no tópico do Pub/Sub, e não uma string. Isso é possível porque aproveitamos o suporte do Spring Cloud GCP para o envio de payloads JSON personalizados para tópicos. As bibliotecas permitem que você serialize objetos para JSON, envie payloads JSON para um tópico e desserialize o payload quando ele for recebido.
Para aproveitar esse recurso, precisamos adicionar um bean ObjectMapper
ao contexto do seu aplicativo. Esse bean ObjectMapper
será usado para serializar objetos de e para o JSON quando seu aplicativo enviar e receber mensagens. Na classe DemoApplication.kt
, adicione o bean Spring JacksonPubSubMessageConverter
:
src/main/kotlin/com/example/demo/DemoApplication.kt
package com.example.demo
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
// new imports to add
import org.springframework.context.annotation.Bean
import com.fasterxml.jackson.databind.ObjectMapper
import com.google.cloud.spring.pubsub.support.converter.JacksonPubSubMessageConverter
@SpringBootApplication
class DemoApplication {
// This bean enables serialization/deserialization of
// Java objects to JSON for Pub/Sub payloads
@Bean
fun jacksonPubSubMessageConverter(objectMapper: ObjectMapper) =
JacksonPubSubMessageConverter(objectMapper)
}
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
Neste ponto, tente executar o aplicativo novamente executando:
$ ./mvnw spring-boot:run
A partir do formulário da Web na página principal, o aplicativo enviará as informações para o tópico do Pub/Sub que você criou. No entanto, ele ainda não está fazendo nada útil porque ainda precisamos ler esse tópico do Pub/Sub. Isso será feito na próxima etapa.
9. como ler registrados como registrados no tópico do Pub/Sub
Na etapa final, vamos processar as informações do responsável pelo registro do tópico do Pub/Sub e manter as informações no banco de dados do Cloud MySQL. A inscrição vai ser concluída, e você poderá enviar novos inscritos pelo formulário e conferir todos os usuários registrados pelo endpoint /registrants
.
Este aplicativo utilizará a Spring Integration, que oferece muitas abstrações convenientes para lidar com mensagens. Adicionaremos um PubSubInboundChannelAdapter
para permitir a leitura das mensagens do tópico do Pub/Sub e colocá-las no pubsubInputChannel
para processamento adicional. Em seguida, configuraremos a função messageReceiver
usando @ServiceActivator
para ser invocada com as mensagens que chegam ao pubsubInputChannel
.
src/main/kotlin/com/example/demo/DemoApplication.kt
package com.example.demo
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import com.fasterxml.jackson.databind.ObjectMapper
import org.springframework.cloud.gcp.pubsub.support.converter.JacksonPubSubMessageConverter
// new imports to add
import com.google.cloud.spring.pubsub.core.PubSubTemplate
import com.google.cloud.spring.pubsub.integration.AckMode
import com.google.cloud.spring.pubsub.integration.inbound.PubSubInboundChannelAdapter
import com.google.cloud.spring.pubsub.support.BasicAcknowledgeablePubsubMessage
import com.google.cloud.spring.pubsub.support.GcpPubSubHeaders
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.integration.annotation.ServiceActivator
import org.springframework.integration.channel.DirectChannel
import org.springframework.messaging.MessageChannel
import org.springframework.messaging.handler.annotation.Header
@SpringBootApplication
class DemoApplication {
private val REGISTRANT_SUBSCRIPTION = "registrations-sub"
@Autowired
private lateinit var personRepository: PersonRepository
// New Spring Beans to add
@Bean
fun pubsubInputChannel() = DirectChannel()
@Bean
fun messageChannelAdapter(
@Qualifier("pubsubInputChannel") inputChannel: MessageChannel,
pubSubTemplate: PubSubTemplate): PubSubInboundChannelAdapter {
val adapter = PubSubInboundChannelAdapter(
pubSubTemplate, REGISTRANT_SUBSCRIPTION)
adapter.outputChannel = inputChannel
adapter.ackMode = AckMode.MANUAL
adapter.payloadType = Person::class.java
return adapter
}
@ServiceActivator(inputChannel = "pubsubInputChannel")
fun messageReceiver(
payload: Person,
@Header(GcpPubSubHeaders.ORIGINAL_MESSAGE) message: BasicAcknowledgeablePubsubMessage) {
personRepository.save(payload)
print("Message arrived! Payload: $payload")
message.ack()
}
// ObjectMapper bean from previous step
@Bean
fun jacksonPubSubMessageConverter(objectMapper: ObjectMapper) = JacksonPubSubMessageConverter(objectMapper)
}
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
Neste ponto, você concluiu a configuração do aplicativo. Para verificar se o app funciona corretamente, execute:
$ ./mvnw spring-boot:run
Clique no botão Visualizar novamente e tente registrar um usuário preenchendo o formulário e enviando.
Clique no link Pessoas registradas para verificar se o novo responsável pelo registro aparece na tabela.
Parabéns, você terminou! Encerre o aplicativo pressionando CTRL+C
na janela do terminal.
10. Limpeza
Para limpar seu ambiente, exclua o tópico do Pub/Sub e a instância do Cloud MySQL que você criou.
Como excluir a instância do Cloud MySQL
$ gcloud sql instances delete codelab-instance
Como excluir os recursos do Pub/Sub
$ gcloud pubsub subscriptions delete registrations-sub $ gcloud pubsub topics delete registrations
11. Parabéns!
Você terminou de criar um aplicativo Spring Kotlin que se integra ao Cloud Pub/Sub e ao Cloud SQL (MySQL).
Saiba mais
- Projeto do Spring no GCP: http://cloud.spring.io/spring-cloud-gcp/
- Repositório do GitHub do Spring no GCP: https://github.com/GoogleCloudPlatform/spring-cloud-gcp
- Java no Google Cloud Platform: https://cloud.google.com/java/
- Exemplos de aplicativos Kotlin usando o GCP: https://github.com/GoogleCloudPlatform/spring-cloud-gcp/tree/master/spring-cloud-gcp-kotlin-samples (em inglês)
Licença
Este conteúdo está sob a licença Atribuição 2.0 Genérica da Creative Commons.