1. Introduzione
Spring Framework 5.0 ha aggiunto il supporto dedicato di Kotlin, semplificando l'utilizzo di Spring per gli sviluppatori Kotlin. Di conseguenza, queste modifiche hanno fatto sì che le integrazioni di Google Cloud fornite da Spring Cloud GCP funzionassero perfettamente anche in Kotlin. In questo codelab vedrai quanto è facile iniziare a utilizzare i servizi Google Cloud nelle tue applicazioni Kotlin.
Questo codelab illustra la configurazione di una semplice applicazione di registrazione in Kotlin che mostra l'utilizzo dei servizi GCP, tra cui Cloud Pub/Sub e Cloud SQL.
Cosa creerai
In questo codelab, configurerai un'applicazione Kotlin Spring Boot che accetta le informazioni dei partecipanti, le pubblica in un argomento Cloud Pub/Sub e le salva in un database Cloud MySQL.
Obiettivi didattici
Come eseguire l'integrazione con i servizi Google Cloud nella tua applicazione Kotlin Spring.
Che cosa ti serve
- Un progetto Google Cloud
- Un browser, ad esempio Chrome o Firefox
Come utilizzerai questo tutorial?
Come valuti la tua esperienza di creazione di app web HTML/CSS?
Come valuti la tua esperienza di utilizzo dei servizi Google Cloud Platform?
2. Configurazione e requisiti
Configurazione dell'ambiente autonomo
- Accedi alla console Cloud e crea un nuovo progetto o riutilizzane uno esistente. Se non hai già un account Gmail o G Suite, devi crearne uno.
Ricorda l'ID progetto, un nome univoco tra tutti i progetti Google Cloud (il nome sopra è già stato utilizzato e non funzionerà per te, mi dispiace). In questo codelab verrà chiamato PROJECT_ID.
- Successivamente, dovrai abilitare la fatturazione in Cloud Console per utilizzare le risorse Google Cloud.
L'esecuzione di questo codelab non dovrebbe costare molto, se non nulla. Assicurati di seguire le istruzioni riportate nella sezione "Pulizia", che ti consiglia come arrestare le risorse in modo da non incorrere in addebiti oltre questo tutorial. I nuovi utenti di Google Cloud possono beneficiare del programma prova senza costi di 300$.
Google Cloud Shell
Sebbene Google Cloud possa essere gestito da remoto dal tuo laptop, in questo codelab utilizzeremo Google Cloud Shell, un ambiente a riga di comando in esecuzione nel cloud.
Attiva Cloud Shell
- Nella console Cloud, fai clic su Attiva Cloud Shell
.
Se non hai mai avviato Cloud Shell, viene visualizzata una schermata intermedia (sotto la piega) che ne descrive le funzionalità. In questo caso, fai clic su Continua e non comparirà più. Ecco come si presenta la schermata intermedia:
Bastano pochi istanti per eseguire il provisioning e connettersi a Cloud Shell.
Questa macchina virtuale è caricata con tutti gli strumenti per sviluppatori di cui avrai bisogno. Offre una home directory permanente da 5 GB e viene eseguita in Google Cloud, migliorando notevolmente le prestazioni e l'autenticazione della rete. Gran parte del lavoro per questo codelab, se non tutto, può essere svolto semplicemente con un browser o con Chromebook.
Una volta eseguita la connessione a Cloud Shell, dovresti vedere che il tuo account è già autenticato e il progetto è già impostato sul tuo ID progetto.
- Esegui questo comando in Cloud Shell per verificare che l'account sia autenticato:
gcloud auth list
Output 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
Output comando
[core] project = <PROJECT_ID>
In caso contrario, puoi impostarlo con questo comando:
gcloud config set project <PROJECT_ID>
Output comando
Updated property [core/project].
3. Provisioning delle risorse Pub/Sub
Innanzitutto, dobbiamo configurare un argomento e una sottoscrizione Cloud Pub/Sub. In questa applicazione, pubblicheremo le informazioni di registrazione in un argomento Pub/Sub; le informazioni vengono quindi lette da questo argomento e rese persistenti in un database.
In questo tutorial, ci affideremo a Cloud Shell per il provisioning delle risorse. Tieni presente che è anche possibile configurare le risorse Pub/Sub tramite la sezione Cloud Pub/Sub in Google Cloud Console.
Nel terminale Cloud Shell, abilita prima l'API Pub/Sub.
$ gcloud services enable pubsub.googleapis.com
Successivamente, creeremo un argomento Pub/Sub denominato registrations per questa applicazione. Le informazioni di registrazione inviate tramite l'applicazione verranno pubblicate in questo argomento.
$ gcloud pubsub topics create registrations
Infine, crea una sottoscrizione per l'argomento. Un abbonamento Pub/Sub ti consente di ricevere messaggi da un argomento.
$ gcloud pubsub subscriptions create registrations-sub --topic=registrations
Ora hai completato la creazione di un argomento e di una sottoscrizione Cloud Pub/Sub per la tua applicazione.
4. Crea un'istanza e un database Cloud SQL (MySQL)
Per la nostra applicazione di esempio, dobbiamo anche configurare un'istanza di database per contenere le informazioni dei registranti. Questo passaggio si basa anche sul terminale Cloud Shell per il provisioning delle risorse Cloud SQL. Tieni presente che puoi visualizzare e configurare le tue istanze Cloud SQL anche tramite la console Google Cloud.
Innanzitutto, abilita l'API Cloud SQL Admin.
$ gcloud services enable sqladmin.googleapis.com
Successivamente, eseguiremo il provisioning di un'istanza Cloud SQL (MySQL). L'esecuzione di questo comando potrebbe richiedere del tempo.
$ gcloud sql instances create codelab-instance --region=us-east1
Dopo aver creato correttamente l'istanza Cloud SQL, crea un nuovo database nell'istanza denominato registrants.
$ gcloud sql databases create registrants --instance codelab-instance
Ora hai completato la configurazione dell'istanza Cloud SQL e del database per la tua applicazione.
5. Inizializzare un'applicazione Spring Boot
Ora siamo pronti per iniziare a scrivere l'applicazione. I passaggi successivi continueranno a utilizzare Cloud Shell descritta nei passaggi di configurazione.
Innanzitutto, utilizzeremo Initializr per generare il codice di scaffolding per il progetto. Nella finestra di Cloud Shell, esegui:
$ 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
Questo comando genera una configurazione iniziale del progetto Maven e il codice di scaffolding per l'applicazione nella directory registrations-codelab/. Le sezioni seguenti descrivono le modifiche al codice necessarie per produrre un'applicazione funzionante.
Editor di codice di Cloud Shell
Il modo più semplice per iniziare a modificare e visualizzare il codice nell'ambiente Cloud Shell è utilizzare l'editor di codice Cloud Shell integrato.
Una volta aperta un'istanza di Cloud Shell, fai clic sull'icona a forma di matita per aprire l'editor di codice. L'editor dovrebbe consentirti di modificare direttamente i file di progetto prodotti da Initialzr.

6. Configurazione per database
Innanzitutto, configura l'applicazione in modo che possa connettersi al database Cloud MySQL che hai configurato. Le librerie Spring Cloud GCP offrono uno starter Cloud MySQL che fornisce le dipendenze necessarie per connettersi a un'istanza Cloud MySQL.
Aggiungi la dipendenza spring-cloud-gcp-starter-sql-mysql al file pom.xml del progetto:
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>
Inoltre, devi modificare il file di configurazione application.properties per descrivere la configurazione del database. Copia le seguenti proprietà nel file application.properties.
Trova il nome di connessione istanza al tuo database:
$ gcloud sql instances describe codelab-instance \ --format 'value(connectionName)'
L'output verrà utilizzato nel file application.properties per configurare le informazioni di connessione.
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
L'unica proprietà che devi modificare è il nome della connessione dell'istanza. Questo valore deve essere formattato come valore separato dai due punti nel formato: YOUR_GCP_PROJECT_ID:REGION:DATABASE_INSTANCE_NAME.
7. Creare i contenuti statici
Innanzitutto, creeremo il frontend della nostra applicazione. L'applicazione deve avere un modulo che consenta a una persona di registrare gli individui e una visualizzazione che mostri tutti gli utenti registrati.
Per la home page, crea un index.html contenente il modulo di registrazione.
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>
Successivamente, creeremo un modello Thymeleaf denominato registrants.html per visualizzare gli utenti registrati. Thymeleaf è un framework di modelli che utilizziamo per creare e pubblicare HTML creato dinamicamente. Vedrai che il modello ha un aspetto simile all'HTML, ma contiene alcuni elementi markdown aggiuntivi per gestire i contenuti dinamici. Questo modello accetta un singolo parametro denominato personsList, che contiene tutti i partecipanti registrati tramite l'applicazione.
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>
A questo punto, puoi verificare che i contenuti statici vengano pubblicati.
Crea ed esegui l'app utilizzando Maven:
$ ./mvnw spring-boot:run
Fai clic sul pulsante di anteprima nella finestra Cloud Shell e verifica che venga visualizzata la home page. Tuttavia, nessuna funzionalità dell'interfaccia utente funzionerà perché manca un controller web. Verrà aggiunto nel passaggio successivo.

Dopo aver visualizzato l'anteprima dell'applicazione, premi CTRL+C per chiuderla.
8. Invio dei partecipanti a un argomento Pub/Sub
In questo passaggio, implementeremo la funzionalità in cui i partecipanti inviati tramite il modulo web verranno pubblicati in un argomento Cloud Pub/Sub.
Aggiungere le classi di dati
Innanzitutto, creeremo alcune classi di dati Kotlin. Queste saranno le nostre entità JPA e fungeranno anche da rappresentazione intermedia dei partecipanti inviati tramite il modulo.
Nel pacchetto demo, aggiungi due nuovi file: una classe Person e un PersonRepository Spring Data. Queste due classi ci consentiranno di archiviare e recuperare facilmente le voci di registrazione dal nostro database MySQL utilizzando 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>
Aggiungere il controller web
A questo punto, creeremo una classe Controller che elabora i partecipanti dal modulo e invia le informazioni all'argomento Cloud Pub/Sub creato in precedenza. Questo controller crea due endpoint:
/registerPerson: l'endpoint POST in cui vengono inviate le informazioni del registrante e poi inviate all'argomento Pub/Sub. Nella funzioneregisterPerson(..), le informazioni del registrante vengono inviate all'argomento Pub/Sub utilizzandoPubSubTemplate, una classe di utilità delle integrazioni Spring Cloud GCP Pub/Sub che riduce al minimo il codice boilerplate necessario per iniziare a interagire con Cloud Pub/Sub./registrants: Mostra tutti i partecipanti registrati correttamente nel database. Queste informazioni vengono recuperate dall'istanza MySQL utilizzando il repository Spring Data che abbiamo creato nel passaggio precedente.
Crea la seguente classe Controller nel pacchetto demo:
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))
}
}
Il controller legge le informazioni del registrante inviate tramite il modulo web e le pubblica nell'argomento Pub/Sub.
Aggiunta del bean JSON Object Mapper
Potresti aver notato nel controller che pubblichiamo un oggetto Person nell'argomento Pub/Sub e non una stringa. Ciò è possibile perché sfruttiamo il supporto di Spring Cloud GCP per i payload JSON personalizzati da inviare agli argomenti. Le librerie consentono di serializzare gli oggetti in JSON, inviare payload JSON a un argomento e deserializzare il payload quando viene ricevuto.
Per usufruire di questa funzionalità, dobbiamo aggiungere un bean ObjectMapper al contesto dell'applicazione. Questo bean ObjectMapper verrà utilizzato per serializzare gli oggetti in formato JSON e viceversa quando l'applicazione invia e riceve messaggi. Nella classe DemoApplication.kt, aggiungi il 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)
}
A questo punto, puoi provare a eseguire di nuovo l'applicazione eseguendo:
$ ./mvnw spring-boot:run
Dal modulo web nella pagina principale, l'applicazione invierà ora le informazioni all'argomento Pub/Sub che hai creato. Tuttavia, non fa ancora nulla di utile perché dobbiamo ancora leggere da quell'argomento Pub/Sub. Questo passaggio viene eseguito nel passaggio successivo.
9. Lettura dei registranti dall'argomento Pub/Sub
Nel passaggio finale, elaboreremo le informazioni dei partecipanti dall'argomento Pub/Sub e le conserveremo nel database Cloud MySQL. In questo modo, l'applicazione verrà completata, consentendoti di inviare nuovi registrati tramite il modulo e visualizzare tutti gli utenti registrati tramite l'endpoint /registrants.
Questa applicazione sfrutterà Spring Integration, che offre molte astrazioni convenienti per la gestione della messaggistica. Aggiungeremo un PubSubInboundChannelAdapter per consentirci di leggere i messaggi dall'argomento Pub/Sub e inserirli in pubsubInputChannel per l'ulteriore elaborazione. Configureremo quindi la funzione messageReceiver utilizzando @ServiceActivator per essere richiamata con i messaggi in arrivo su 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)
}
A questo punto, hai completato la configurazione dell'applicazione. Per verificare che l'app funzioni correttamente, esegui:
$ ./mvnw spring-boot:run
Fai di nuovo clic sul pulsante Anteprima e prova a registrare un utente compilando il modulo e inviandolo.

Fai clic sul link Persone registrate per verificare che il nuovo utente registrato venga visualizzato nella tabella.

Congratulazioni, ora hai finito. Interrompi l'applicazione premendo CTRL+C nella finestra del terminale.
10. Esegui la pulizia
Per pulire l'ambiente, devi eliminare l'argomento Pub/Sub e l'istanza Cloud MySQL che hai creato.
Eliminazione dell'istanza Cloud MySQL
$ gcloud sql instances delete codelab-instance
Eliminazione delle risorse Pub/Sub
$ gcloud pubsub subscriptions delete registrations-sub $ gcloud pubsub topics delete registrations
11. Complimenti!
Ora hai completato la scrittura di un'applicazione Spring Kotlin che si integra con Cloud Pub/Sub e Cloud SQL (MySQL).
Scopri di più
- Progetto Spring su GCP: http://cloud.spring.io/spring-cloud-gcp/
- Repository GitHub di Spring su GCP: https://github.com/GoogleCloudPlatform/spring-cloud-gcp
- Java su Google Cloud: https://cloud.google.com/java/
- Applicazioni Kotlin di esempio che utilizzano GCP: https://github.com/GoogleCloudPlatform/spring-cloud-gcp/tree/master/spring-cloud-gcp-kotlin-samples
Licenza
Questo lavoro è concesso in licenza ai sensi di una licenza Creative Commons Attribution 2.0 Generic.