1. Introducción
Spring Framework 5.0 agregó compatibilidad exclusiva con Kotlin para que los desarrolladores de Kotlin puedan usar Spring con facilidad. En consecuencia, estos cambios significaron que las integraciones de Google Cloud proporcionadas por Spring Cloud GCP también funcionaron a la perfección en Kotlin. En este codelab, descubrirás lo fácil que es comenzar a usar los servicios de Google Cloud en tus aplicaciones de Kotlin.
En este codelab, se explica cómo configurar una aplicación de registro simple en Kotlin que demuestra el uso de los servicios de GCP, incluidos Cloud Pub/Sub y Cloud SQL.
Qué compilarás
En este codelab, configurarás una aplicación de Kotlin Spring Boot que acepte la información del registrante, la publique en un tema de Cloud Pub/Sub y la conserve en una base de datos de Cloud MySQL.
Qué aprenderás
Cómo realizar la integración con los servicios de Google Cloud en tu aplicación de Kotlin Spring
Requisitos
- Un proyecto de Google Cloud Platform
- Un navegador, como Chrome o Firefox
¿Cómo usarás este instructivo?
¿Cómo calificarías tu experiencia con la creación de aplicaciones web HTML/CSS?
¿Cómo calificarías tu experiencia en el uso de los servicios de Google Cloud Platform?
2. Configuración y requisitos
Configuración del entorno de autoaprendizaje
- Accede a la consola de Cloud y crea un proyecto nuevo o reutiliza uno existente. (Si todavía no tienes una cuenta de Gmail o de G Suite, debes crear una).
Recuerde el ID de proyecto, un nombre único en todos los proyectos de Google Cloud (el nombre anterior ya se encuentra en uso y no lo podrá usar). Se mencionará más adelante en este codelab como PROJECT_ID
.
- A continuación, deberás habilitar la facturación en la consola de Cloud para usar los recursos de Google Cloud recursos.
Ejecutar este codelab no debería costar mucho, tal vez nada. Asegúrate de seguir las instrucciones de la sección “Realiza una limpieza” en la que se aconseja cómo cerrar recursos para no incurrir en facturación más allá de este instructivo. 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
- En la consola de Cloud, haz clic en Activar Cloud Shell.
Si nunca ha iniciado Cloud Shell, aparecerá una pantalla intermedia (debajo de la mitad inferior de la página) que describe qué es. Si ese es el caso, haz clic en Continuar (y no volverás a verlo). Así es como se ve la pantalla única:
El aprovisionamiento y la conexión a Cloud Shell solo tomará unos minutos.
Esta máquina virtual está cargada con todas las herramientas de desarrollo que necesitarás. Ofrece un directorio principal persistente de 5 GB y se ejecuta en Google Cloud, lo que permite mejorar considerablemente el rendimiento de la red y la autenticación. Gran parte de tu trabajo en este codelab, si no todo, se puede hacer simplemente con un navegador o tu Chromebook.
Una vez conectado a Cloud Shell, debería ver que ya se autenticó y que el proyecto ya se configuró con tu ID del proyecto.
- En Cloud Shell, ejecuta el siguiente comando para confirmar que está autenticado:
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`
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
Primero, necesitaremos configurar un tema y una suscripción de Cloud Pub/Sub. En esta aplicación, publicaremos información de registro en un tema de Pub/Sub. la información se lee de este tema y se conserva en una base de datos.
En este instructivo, nos basaremos en Cloud Shell para aprovisionar nuestros recursos. Ten en cuenta que también se pueden configurar los recursos de Pub/Sub a través de la sección de Cloud Pub/Sub en la consola de Google Cloud.
En tu terminal de Cloud Shell, primero habilita la API de Pub/Sub.
$ gcloud services enable pubsub.googleapis.com
A continuación, crearemos un tema de Pub/Sub llamado registrations
para esta aplicación. La información de registro que se envíe a través de la solicitud se publicará en este tema.
$ gcloud pubsub topics create registrations
Por último, crea una suscripción para el tema. Una suscripción a Pub/Sub te permite recibir mensajes de un tema.
$ gcloud pubsub subscriptions create registrations-sub --topic=registrations
Ya completaste la creación de un tema y una suscripción de Cloud Pub/Sub para tu aplicación.
4. Crea una instancia y una base de datos de Cloud SQL (MySQL)
Para nuestra aplicación de muestra, también necesitamos configurar una instancia de base de datos que contenga la información del registrante. Este paso también dependerá de la terminal de Cloud Shell para aprovisionar los recursos de Cloud SQL. Ten en cuenta que también puedes ver y configurar tus instancias de Cloud SQL a través de la consola de Google Cloud.
Primero, habilita la API de Cloud SQL Admin.
$ gcloud services enable sqladmin.googleapis.com
A continuación, aprovisionaremos una instancia de Cloud SQL (MySQL). Este comando puede tardar un poco.
$ gcloud sql instances create codelab-instance --region=us-east1
Después de crear correctamente tu instancia de Cloud SQL, crea una nueva base de datos en tu instancia llamada registrants
.
$ gcloud sql databases create registrants --instance codelab-instance
Ya completaste la configuración de la instancia y la base de datos de Cloud SQL para tu aplicación.
5. Inicializa una aplicación de Spring Boot
Ahora estamos listos para comenzar a escribir la aplicación. En los próximos pasos, se seguirá usando Cloud Shell descrito en los pasos de configuración.
Primero, usaremos Initializr para generar el código de andamiaje del proyecto. En la ventana de Cloud Shell, ejecuta lo siguiente:
$ 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
Con este comando, se genera una configuración inicial del proyecto de Maven, así como un código de andamiaje para tu aplicación en el directorio registrations-codelab/
. En las siguientes secciones, se describen las ediciones de código necesarias para producir una aplicación que funcione.
Editor de código de Cloud Shell
La forma más sencilla de comenzar a modificar y ver el código en el entorno de Cloud Shell es usar el editor de código de Cloud Shell integrado.
Una vez que hayas abierto una instancia de Cloud Shell, haz clic en el ícono de lápiz para abrir el editor de código. El editor debería permitirte modificar directamente los archivos del proyecto producidos por Initialzr.
6. Configuración de la base de datos
Primero, configura tu aplicación para que pueda conectarse a la base de datos de Cloud MySQL que configuraste. Las bibliotecas de Spring Cloud GCP ofrecen un activador de Cloud MySQL que proporciona las dependencias necesarias para conectarse a una instancia de Cloud MySQL.
Agrega la dependencia spring-cloud-gcp-starter-sql-mysql
al proyecto pom.xml:
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>
Además, debes modificar el archivo de configuración application.properties
para describir la configuración de tu base de datos. Copia las siguientes propiedades en tu archivo application.properties
.
Busca el nombre de conexión de la instancia con tu base de datos:
$ gcloud sql instances describe codelab-instance \ --format 'value(connectionName)'
El resultado se usará en el archivo application.properties
para configurar la información de conexión.
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
La única propiedad que debes modificar es el nombre de la conexión con la instancia. Este valor debe expresarse como un valor separado por dos puntos con el siguiente formato: YOUR_GCP_PROJECT_ID:REGION:DATABASE_INSTANCE_NAME
.
7. Creación del contenido estático
Primero, crearemos el frontend para nuestra aplicación. La solicitud debe tener un formulario que permita a alguien registrar personas físicas y, además, una vista que muestre todas las personas registradas correctamente.
Para la página principal, crea una index.html
que contenga el formulario 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>
A continuación, crearemos una plantilla Thymeleaf llamada registrants.html
para mostrar los usuarios registrados. Thymeleaf es un framework de plantillas que usamos para construir y publicar código HTML creado de forma dinámica. Verás que la plantilla se ve como HTML, excepto que tiene algunos elementos de Markdown adicionales para administrar contenido dinámico. Esta plantilla acepta un solo parámetro llamado personsList
, que contiene todos los registrantes que se registraron a través de la aplicación.
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>
En este punto, puedes verificar que se esté entregando el contenido estático.
Compila y ejecuta la app con Maven:
$ ./mvnw spring-boot:run
Haz clic en el botón Vista previa de la ventana de Cloud Shell y verifica que veas la página principal renderizada. Sin embargo, ninguna de las funciones de la IU funcionará porque nos falta un controlador web. Se agregará en el siguiente paso.
Después de obtener una vista previa de la aplicación, presiona CTRL+C
para cerrarla.
8. Cómo enviar a los registrantes a un tema de Pub/Sub
En este paso, implementaremos la función cuando los registrantes enviados a través del formulario web se publicarán en un tema de Cloud Pub/Sub.
Agrega las clases de datos
Primero, crearemos algunas clases de datos de Kotlin: Estas serán nuestras entidades JPA y también actuarán como nuestra representación intermedia de las personas registradas enviadas a través del formulario.
En el paquete de demostración, agrega dos archivos nuevos: una clase Person
y un PersonRepository
de Spring Data. Estas dos clases nos permitirán almacenar y recuperar con facilidad entradas de registro de la base de datos de MySQL con 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>
Agrega el controlador web
A continuación, crearemos una clase de controlador que procesa a los registrantes del formulario y envía la información al tema de Cloud Pub/Sub que creó anteriormente. Este controlador crea dos extremos:
/registerPerson
: Es el extremo POST en el que se envía la información del registrante y, luego, se envía al tema de Pub/Sub. En la funciónregisterPerson(..)
, la información del registrante se envía al tema de Pub/Sub a través dePubSubTemplate
, una clase de conveniencia de las integraciones de Pub/Sub de Spring Cloud GCP que minimiza el código estándar necesario para comenzar a interactuar con Cloud Pub/Sub./registrants
: Muestra todos los registrantes que se registraron correctamente en la base de datos. Esta información se recupera de la instancia de MySQL con el repositorio de Spring Data que creamos en el paso anterior.
Crea la siguiente clase de controlador en el paquete de demostración:
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))
}
}
El controlador lee la información del registrante enviada a través del formulario web y, luego, la publica en el tema de Pub/Sub.
Agrega el Bean del asignador de objetos JSON
Es posible que hayas notado en el controlador que publicamos un objeto Person
en el tema de Pub/Sub y no en una cadena. Esto es posible porque aprovechamos la compatibilidad de Spring Cloud GCP para que las cargas útiles personalizadas de JSON se envíen a los temas. Las bibliotecas te permiten serializar objetos a JSON, enviar cargas útiles de JSON a un tema y deserializar la carga útil cuando se recibe.
Para aprovechar esta función, debemos agregar un bean ObjectMapper
al contexto de tu aplicación. Este bean ObjectMapper
se usará para serializar objetos hacia JSON y desde este cuando tu aplicación envía y recibe mensajes. En la clase DemoApplication.kt
, agrega Spring bean 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)
}
En este punto, puedes intentar ejecutar la aplicación nuevamente con el siguiente comando:
$ ./mvnw spring-boot:run
Desde el formulario web de la página principal, la aplicación enviará la información al tema de Pub/Sub que creaste. Sin embargo, aún no hace nada útil porque todavía necesitamos leer desde ese tema de Pub/Sub. Esto se logra en el siguiente paso.
9. Lee los registrantes del tema de Pub/Sub
En el último paso, procesaremos la información del registrante del tema de Pub/Sub y conservaremos la información en la base de datos de Cloud MySQL. Esta acción completará la solicitud, lo que te permitirá enviar nuevas personas registradas a través del formulario y ver a todos los usuarios registrados a través del extremo /registrants
.
Esta aplicación aprovechará Spring Integration, que ofrece muchas abstracciones convenientes para administrar la mensajería. Agregaremos una PubSubInboundChannelAdapter
para permitirnos leer mensajes del tema de Pub/Sub y colocarlos en pubsubInputChannel
para continuar con el procesamiento. Luego, configuraremos la función messageReceiver
con @ServiceActivator
para que se invoque con los mensajes que lleguen a 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)
}
En este punto, ya completaste la configuración de la aplicación. Para verificar que la app funcione correctamente, ejecuta lo siguiente:
$ ./mvnw spring-boot:run
Vuelve a hacer clic en el botón Vista previa y completa el formulario y envíalo para registrar un usuario.
Haga clic en el vínculo Personas registradas para verificar que el nuevo registrante aparezca en la tabla.
¡Felicitaciones! Eso es todo. Para finalizar la aplicación, presiona CTRL+C
en la ventana de terminal.
10. Limpieza
Para limpiar tu entorno, debes borrar el tema de Pub/Sub y la instancia de Cloud MySQL que creaste.
Borra la instancia de Cloud MySQL
$ gcloud sql instances delete codelab-instance
Borra los recursos de Pub/Sub
$ gcloud pubsub subscriptions delete registrations-sub $ gcloud pubsub topics delete registrations
11. ¡Felicitaciones!
Ya terminaste de escribir una aplicación de Spring Kotlin que se integra con Cloud Pub/Sub y Cloud SQL (MySQL).
Más información
- Proyecto Spring en GCP: http://cloud.spring.io/spring-cloud-gcp/
- Spring en el repositorio de GitHub de GCP: https://github.com/GoogleCloudPlatform/spring-cloud-gcp
- Java en Google Cloud Platform: https://cloud.google.com/java/
- Aplicaciones de Kotlin de muestra con GCP: https://github.com/GoogleCloudPlatform/spring-cloud-gcp/tree/master/spring-cloud-gcp-kotlin-samples
Licencia
Este trabajo cuenta con una licencia Atribución 2.0 Genérica de Creative Commons.