1. Introduction
Le framework Spring 5.0 est compatible avec Kotlin dédié, ce qui facilite l'utilisation de Spring pour les développeurs Kotlin. Par conséquent, avec ces modifications, les intégrations Google Cloud fournies par Spring Cloud GCP fonctionnent également parfaitement en Kotlin. Dans cet atelier de programmation, vous verrez à quel point il est facile de commencer à utiliser les services Google Cloud dans vos applications Kotlin.
Cet atelier de programmation explique comment configurer en Kotlin une application d'enregistrement simple qui illustre l'utilisation de services GCP tels que Cloud Pub/Sub et Cloud SQL.
Ce que vous allez faire
Dans cet atelier de programmation, vous allez configurer une application Kotlin Spring Boot qui accepte les informations du titulaire, les publie dans un sujet Cloud Pub/Sub et les conserve dans une base de données Cloud MySQL.
Points abordés
Intégrer les services Google Cloud à votre application Spring Kotlin
Prérequis
- Un projet Google Cloud Platform
- Un navigateur tel que Chrome ou Firefox
Comment allez-vous utiliser ce tutoriel ?
Comment évalueriez-vous votre expérience en matière de création d'applications Web HTML/CSS ?
Quel est votre niveau d'expérience avec les services Google Cloud Platform ?
<ph type="x-smartling-placeholder">2. Préparation
Configuration de l'environnement d'auto-formation
- Connectez-vous à Cloud Console, puis créez un projet ou réutilisez un projet existant. (Si vous n'avez pas encore de compte Gmail ou G Suite, vous devez en créer un.)
Mémorisez l'ID du projet. Il s'agit d'un nom unique permettant de différencier chaque projet Google Cloud (le nom ci-dessus est déjà pris ; vous devez en trouver un autre). Il sera désigné par le nom PROJECT_ID
tout au long de cet atelier de programmation.
- Vous devez ensuite activer la facturation dans Cloud Console pour pouvoir utiliser les ressources Google Cloud.
L'exécution de cet atelier de programmation est très peu coûteuse, voire gratuite. Veillez à suivre les instructions de la section "Nettoyer" qui indique comment désactiver les ressources afin d'éviter les frais une fois ce tutoriel terminé. Les nouveaux utilisateurs de Google Cloud peuvent participer au programme d'essai sans frais pour bénéficier d'un crédit de 300 $.
Google Cloud Shell
Bien que Google Cloud puisse être utilisé à distance depuis votre ordinateur portable, nous allons utiliser Google Cloud Shell dans cet atelier de programmation, un environnement de ligne de commande exécuté dans le cloud.
Activer Cloud Shell
- Dans Cloud Console, cliquez sur Activer Cloud Shell .
Si vous n'avez encore jamais démarré Cloud Shell, un écran intermédiaire s'affiche en dessous de la ligne de séparation pour décrire de quoi il s'agit. Si tel est le cas, cliquez sur Continuer (cet écran ne s'affiche qu'une seule fois). Voici à quoi il ressemble :
Le provisionnement et la connexion à Cloud Shell ne devraient pas prendre plus de quelques minutes.
Cette machine virtuelle contient tous les outils de développement nécessaires. Elle intègre un répertoire d'accueil persistant de 5 Go et s'exécute sur Google Cloud, ce qui améliore nettement les performances réseau et l'authentification. Vous pouvez réaliser une grande partie, voire la totalité, des activités de cet atelier dans un simple navigateur ou sur votre Chromebook.
Une fois connecté à Cloud Shell, vous êtes en principe authentifié et le projet est défini avec votre ID de projet.
- Exécutez la commande suivante dans Cloud Shell pour vérifier que vous êtes authentifié :
gcloud auth list
Résultat de la commande
Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com> To set the active account, run: $ gcloud config set account `ACCOUNT`
gcloud config list project
Résultat de la commande
[core] project = <PROJECT_ID>
Si vous obtenez un résultat différent, exécutez cette commande :
gcloud config set project <PROJECT_ID>
Résultat de la commande
Updated property [core/project].
3. Provisionner des ressources Pub/Sub
Nous devons d'abord configurer un sujet et un abonnement Cloud Pub/Sub. Dans cette demande, nous allons publier les informations d'inscription dans un sujet Pub/Sub. les informations sont ensuite lues à partir de ce sujet et conservées dans une base de données.
Dans ce tutoriel, nous allons nous appuyer sur Cloud Shell pour provisionner nos ressources. Notez que vous pouvez également configurer des ressources Pub/Sub via la section Cloud Pub/Sub de la console Google Cloud.
Dans votre terminal Cloud Shell, commencez par activer l'API Pub/Sub.
$ gcloud services enable pubsub.googleapis.com
Nous allons maintenant créer un sujet Pub/Sub nommé registrations
pour cette application. Les informations d'inscription envoyées via la demande seront publiées sur ce sujet.
$ gcloud pubsub topics create registrations
Enfin, créez un abonnement pour le sujet. Un abonnement Pub/Sub vous permet de recevoir des messages à partir d'un sujet.
$ gcloud pubsub subscriptions create registrations-sub --topic=registrations
Vous avez maintenant terminé la création d'un sujet et d'un abonnement Cloud Pub/Sub pour votre application.
4. Créer une instance et une base de données Cloud SQL (MySQL)
Pour notre exemple d'application, nous devons également configurer une instance de base de données pour stocker les informations du titulaire. Cette étape repose également sur le terminal Cloud Shell pour provisionner les ressources Cloud SQL. Notez que vous pouvez également afficher et configurer vos instances Cloud SQL via la console Google Cloud.
Commencez par activer l'API Cloud SQL Admin.
$ gcloud services enable sqladmin.googleapis.com
Vous allez ensuite provisionner une instance Cloud SQL (MySQL). Cette commande peut prendre un certain temps.
$ gcloud sql instances create codelab-instance --region=us-east1
Une fois votre instance Cloud SQL créée, créez une base de données dans votre instance nommée registrants
.
$ gcloud sql databases create registrants --instance codelab-instance
Vous avez maintenant terminé la configuration de l'instance et de la base de données Cloud SQL pour votre application.
5. Initialiser une application Spring Boot
Nous sommes maintenant prêts à commencer à écrire l'application. Dans les étapes suivantes, vous continuerez à utiliser Cloud Shell décrit dans la procédure de configuration.
Nous allons commencer par utiliser Initializr pour générer le code d'échafaudage du projet. Dans votre fenêtre Cloud Shell, exécutez la commande suivante:
$ 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
Cette commande génère une configuration initiale de projet Maven ainsi qu'un code d'échafaudage pour votre application dans le répertoire registrations-codelab/
. Les sections suivantes décrivent les modifications de code nécessaires pour produire une application fonctionnelle.
Éditeur de code Cloud Shell
Le moyen le plus simple de commencer à modifier et à afficher le code dans l'environnement Cloud Shell consiste à utiliser l'éditeur de code Cloud Shell intégré.
Une fois que vous avez ouvert une instance Cloud Shell, cliquez sur l'icône en forme de crayon pour ouvrir l'éditeur de code. L'éditeur doit vous permettre de modifier directement les fichiers du projet produits par Initialzr.
6. Configuration de la base de données
Commencez par configurer votre application afin qu'elle puisse se connecter à la base de données Cloud MySQL que vous avez configurée. Les bibliothèques Spring Cloud GCP proposent un déclencheur Cloud MySQL qui fournit les dépendances nécessaires à la connexion à une instance Cloud MySQL.
Ajoutez la dépendance spring-cloud-gcp-starter-sql-mysql
au fichier pom.xml du projet:
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>
En outre, vous devez modifier le fichier de configuration application.properties
pour décrire la configuration de votre base de données. Copiez les propriétés suivantes dans le fichier application.properties
.
Recherchez le nom de connexion de l'instance à votre base de données:
$ gcloud sql instances describe codelab-instance \ --format 'value(connectionName)'
Le résultat de cette commande sera utilisé dans le fichier application.properties
pour configurer les informations de connexion.
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 seule propriété que vous devez modifier est le nom de connexion de l'instance. Cette valeur doit suivre le format suivant: YOUR_GCP_PROJECT_ID:REGION:DATABASE_INSTANCE_NAME
.
7. Créer le contenu statique
Commençons par créer l'interface de notre application. La demande doit comporter un formulaire permettant à quelqu'un d'enregistrer des personnes physiques, ainsi qu'une vue affichant tous les participants qui ont accepté la demande.
Pour la page d'accueil, créez un index.html
contenant le formulaire d'inscription.
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>
Nous allons maintenant créer un modèle Thymeleaf nommé registrants.html
pour afficher les utilisateurs inscrits. Thymeleaf est un framework de création de modèles que nous utilisons pour construire et diffuser du code HTML créé de façon dynamique. Le modèle se présente au format HTML, sauf qu'il comporte des éléments Markdown supplémentaires pour gérer le contenu dynamique. Ce modèle accepte un seul paramètre appelé personsList
, qui contient tous les participants enregistrés via l'application.
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>
À ce stade, vous pouvez vérifier que le contenu statique est diffusé.
Créez et exécutez l'application à l'aide de Maven:
$ ./mvnw spring-boot:run
Dans la fenêtre Cloud Shell, cliquez sur le bouton d'aperçu et vérifiez que la page d'accueil s'affiche. Cependant, aucune des fonctionnalités de l'interface utilisateur ne fonctionnera, car il manque un contrôleur Web. Cette information sera ajoutée à l'étape suivante.
Après avoir prévisualisé l'application, appuyez sur CTRL+C
pour l'arrêter.
8. Envoyer les titulaires vers un sujet Pub/Sub
Au cours de cette étape, nous allons implémenter la fonctionnalité qui permet de publier dans un sujet Cloud Pub/Sub les participants envoyés via le formulaire Web.
Ajouter les classes de données
Nous allons commencer par créer des classes de données Kotlin. il s'agira de nos entités JPA et servira également de représentation intermédiaire des Participants soumis via le formulaire.
Dans le package de démonstration, ajoutez deux fichiers: une classe Person
et une PersonRepository
Spring Data. Ces deux classes nous permettront de stocker et de récupérer facilement les entrées d'enregistrement de notre base de données MySQL à l'aide de 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>
Ajouter le contrôleur Web
Nous allons ensuite créer une classe Contrôleur qui traitera les titulaires du formulaire et enverra les informations au sujet Cloud Pub/Sub que vous avez créé précédemment. Ce contrôleur crée deux points de terminaison:
/registerPerson
: point de terminaison POST dans lequel les informations du titulaire sont envoyées, puis envoyées au sujet Pub/Sub. Dans la fonctionregisterPerson(..)
, les informations du titulaire sont envoyées au sujet Pub/Sub à l'aide dePubSubTemplate
, une classe pratique issue des intégrations Pub/Sub Spring Cloud GCP qui réduit le code récurrent nécessaire pour commencer à interagir avec Cloud Pub/Sub./registrants
: affiche tous les titulaires correctement enregistrés dans la base de données. Ces informations sont extraites de l'instance MySQL à l'aide du dépôt Spring Data que nous avons créé à l'étape précédente.
Créez la classe Contrôleur suivante dans le package de démonstration:
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))
}
}
Le contrôleur lit les informations du titulaire envoyées via le formulaire Web, puis les publie dans le sujet Pub/Sub.
Ajouter le bean de l'API JSON Object Mapper
Vous avez peut-être remarqué dans le contrôleur que nous publions un objet Person
dans le sujet Pub/Sub et non une chaîne. Cela est possible grâce à la compatibilité de Spring Cloud GCP avec les charges utiles JSON personnalisées à envoyer aux sujets. Les bibliothèques vous permettent de sérialiser des objets au format JSON, d'envoyer des charges utiles JSON à un sujet et de désérialiser la charge utile à sa réception.
Pour profiter de cette fonctionnalité, nous devons ajouter un bean ObjectMapper
au contexte de votre application. Ce bean ObjectMapper
permet de sérialiser des objets vers et depuis JSON lorsque votre application envoie et reçoit des messages. Dans la classe DemoApplication.kt
, ajoutez le 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)
}
À ce stade, vous pouvez réessayer d'exécuter l'application en exécutant la commande suivante:
$ ./mvnw spring-boot:run
À partir du formulaire Web de la page principale, l'application enverra désormais les informations au sujet Pub/Sub que vous avez créé. Cependant, il ne fait encore rien d'utile, car nous avons encore besoin de lire ce sujet Pub/Sub. Nous le ferons à l'étape suivante.
9. Lire les inscrits à partir du sujet Pub/Sub
Pour la dernière étape, nous traiterons les informations du titulaire issues du sujet Pub/Sub et les conserverons dans la base de données Cloud MySQL. La demande d'inscription sera ainsi finalisée. Vous pourrez ainsi envoyer de nouveaux inscrits via le formulaire et afficher tous les utilisateurs enregistrés via le point de terminaison /registrants
.
Cette application tire parti de l'intégration de Spring, qui offre de nombreuses abstractions pratiques pour gérer la messagerie. Nous allons ajouter un PubSubInboundChannelAdapter
pour lire les messages du sujet Pub/Sub et les placer sur pubsubInputChannel
pour un traitement plus poussé. Nous configurerons ensuite la fonction messageReceiver
à l'aide de @ServiceActivator
pour qu'elle soit appelée avec les messages arrivant sur 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)
}
À ce stade, vous avez terminé la configuration de l'application. Pour vérifier que l'application fonctionne correctement, exécutez la commande suivante:
$ ./mvnw spring-boot:run
Cliquez à nouveau sur le bouton Aperçu, puis essayez d'enregistrer un utilisateur en remplissant le formulaire et en l'envoyant.
Cliquez sur le lien Registered People (Personnes enregistrées) pour vérifier que le nouveau titulaire apparaît dans le tableau.
Félicitations, vous avez terminé ! Arrêtez l'application en appuyant sur CTRL+C
dans la fenêtre de terminal.
10. Nettoyage
Pour nettoyer votre environnement, vous devez supprimer le sujet Pub/Sub et l'instance Cloud MySQL que vous avez créés.
Supprimer l'instance Cloud MySQL
$ gcloud sql instances delete codelab-instance
Supprimer les ressources Pub/Sub
$ gcloud pubsub subscriptions delete registrations-sub $ gcloud pubsub topics delete registrations
11. Félicitations !
Vous avez maintenant terminé l'écriture d'une application Kotlin Spring qui s'intègre à Cloud Pub/Sub et Cloud SQL (MySQL).
En savoir plus
- Projet Spring sur GCP: http://cloud.spring.io/spring-cloud-gcp/
- Dépôt GitHub Spring sur GCP: https://github.com/GoogleCloudPlatform/spring-cloud-gcp
- Java sur Google Cloud Platform: https://cloud.google.com/java/
- Exemples d'applications Kotlin utilisant GCP: https://github.com/GoogleCloudPlatform/spring-cloud-gcp/tree/master/spring-cloud-gcp-kotlin-samples
Licence
Ce document est publié sous une licence Creative Commons Attribution 2.0 Generic.