1. Wprowadzenie
W Spring Framework 5.0 dodano specjalną obsługę języka Kotlin, dzięki czemu deweloperzy Kotlin mogą łatwo korzystać z Spring. W rezultacie te zmiany sprawiły, że integracje Google Cloud udostępniane przez Spring Cloud GCP działają bezproblemowo również w Kotlinie. Z tego laboratorium dowiesz się, jak łatwo zacząć korzystać z usług Google Cloud w aplikacjach Kotlin.
W tym laboratorium kodowania pokazujemy, jak skonfigurować prostą aplikację do rejestracji w języku Kotlin, która demonstruje korzystanie z usług GCP, takich jak Cloud Pub/Sub i Cloud SQL.
Co utworzysz
W tym laboratorium nauczysz się konfigurować aplikację Kotlin Spring Boot, która akceptuje informacje o rejestrujących się osobach, publikuje je w temacie Cloud Pub/Sub i zapisuje w bazie danych Cloud MySQL.
Czego się nauczysz
Jak przeprowadzić integrację z usługami Google Cloud w aplikacji Kotlin Spring.
Czego potrzebujesz
- Projekt Google Cloud Platform
- przeglądarkę, np. Chrome lub Firefox;
Jak zamierzasz korzystać z tego samouczka?
Jak oceniasz swoje doświadczenie w tworzeniu aplikacji internetowych w HTML/CSS?
Jak oceniasz korzystanie z usług Google Cloud Platform?
2. Konfiguracja i wymagania
Samodzielne konfigurowanie środowiska
- Zaloguj się w konsoli Google Cloud i utwórz nowy projekt lub użyj istniejącego. (Jeśli nie masz jeszcze konta Gmail lub G Suite, musisz je utworzyć).
Zapamiętaj identyfikator projektu, czyli unikalną nazwę we wszystkich projektach Google Cloud (podana powyżej nazwa jest już zajęta i nie będzie działać w Twoim przypadku). W dalszej części tego laboratorium będzie on nazywany PROJECT_ID.
- Następnie musisz włączyć rozliczenia w konsoli Cloud, aby korzystać z zasobów Google Cloud.
Ukończenie tego laboratorium nie powinno wiązać się z dużymi kosztami, a nawet z żadnymi. Wykonaj instrukcje z sekcji „Czyszczenie”, w której znajdziesz informacje o tym, jak wyłączyć zasoby, aby uniknąć naliczenia opłat po zakończeniu tego samouczka. Nowi użytkownicy Google Cloud mogą skorzystać z programu bezpłatnego okresu próbnego, w którym mają do dyspozycji środki w wysokości 300 USD.
Google Cloud Shell
Z Google Cloud można korzystać zdalnie na laptopie, ale w tym module użyjemy Google Cloud Shell, czyli środowiska wiersza poleceń działającego w chmurze.
Aktywowanie Cloud Shell
- W konsoli Cloud kliknij Aktywuj Cloud Shell
.
Jeśli uruchamiasz Cloud Shell po raz pierwszy, zobaczysz ekran pośredni (część strony widoczna po przewinięciu) z opisem tego środowiska. W takim przypadku kliknij Dalej, a ten ekran nie będzie się już wyświetlać. Ten wyświetlany jednorazowo ekran wygląda tak:
Uzyskanie dostępu do środowiska Cloud Shell i połączenie się z nim powinno zająć tylko kilka chwil.
Ta maszyna wirtualna zawiera wszystkie potrzebne narzędzia dla programistów. Zawiera również stały katalog domowy o pojemności 5 GB i działa w Google Cloud, co znacznie zwiększa wydajność sieci i usprawnia proces uwierzytelniania. Większość zadań w tym module, a być może wszystkie, możesz wykonać w przeglądarce lub na Chromebooku.
Po połączeniu z Cloud Shell zobaczysz, że uwierzytelnianie zostało już przeprowadzone, a projekt jest już ustawiony na Twój identyfikator projektu.
- Aby potwierdzić, że uwierzytelnianie zostało przeprowadzone, uruchom w Cloud Shell to polecenie:
gcloud auth list
Wynik polecenia
Credentialed Accounts
ACTIVE ACCOUNT
* <my_account>@<my_domain.com>
To set the active account, run:
$ gcloud config set account `ACCOUNT`
gcloud config list project
Wynik polecenia
[core] project = <PROJECT_ID>
Jeśli nie, możesz go ustawić za pomocą tego polecenia:
gcloud config set project <PROJECT_ID>
Wynik polecenia
Updated property [core/project].
3. Provision Pub/Sub resources
Najpierw musimy skonfigurować temat i subskrypcję Cloud Pub/Sub. W tej aplikacji będziemy publikować informacje o rejestracji w temacie Pub/Sub. Informacje te będą następnie odczytywane z tego tematu i zapisywane w bazie danych.
W tym samouczku do udostępniania zasobów będziemy używać Cloud Shell. Pamiętaj, że zasoby Pub/Sub możesz też skonfigurować w sekcji Cloud Pub/Sub w konsoli Google Cloud.
W terminalu Cloud Shell najpierw włącz interfejs Pub/Sub API.
$ gcloud services enable pubsub.googleapis.com
Następnie utworzymy dla tej aplikacji temat Pub/Sub o nazwie registrations. Informacje rejestracyjne przesłane za pomocą aplikacji zostaną opublikowane w tym temacie.
$ gcloud pubsub topics create registrations
Na koniec utwórz subskrypcję tematu. Subskrypcja Pub/Sub umożliwia odbieranie wiadomości z tematu.
$ gcloud pubsub subscriptions create registrations-sub --topic=registrations
Temat i subskrypcja Cloud Pub/Sub dla aplikacji zostały utworzone.
4. Tworzenie instancji i bazy danych Cloud SQL (MySQL)
W przypadku naszej przykładowej aplikacji musimy też skonfigurować instancję bazy danych, w której będą przechowywane informacje o osobach rejestrujących się. W tym kroku do aprowizacji zasobów Cloud SQL użyjesz też terminala Cloud Shell. Pamiętaj, że instancje Cloud SQL możesz też wyświetlać i konfigurować w konsoli Google Cloud.
Najpierw włącz interfejs Cloud SQL Admin API.
$ gcloud services enable sqladmin.googleapis.com
Następnie udostępnimy instancję Cloud SQL (MySQL). Wykonanie tego polecenia może trochę potrwać.
$ gcloud sql instances create codelab-instance --region=us-east1
Po utworzeniu instancji Cloud SQL utwórz w niej nową bazę danych o nazwie registrants.
$ gcloud sql databases create registrants --instance codelab-instance
Konfigurowanie instancji i bazy danych Cloud SQL dla aplikacji zostało zakończone.
5. Inicjowanie aplikacji Spring Boot
Możemy teraz zacząć pisać aplikację. W dalszych krokach będziemy korzystać z Cloud Shell opisanego w instrukcji konfiguracji.
Najpierw użyjemy Initializr, aby wygenerować kod szkieletowy projektu. W oknie Cloud Shell uruchom:
$ 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
To polecenie generuje początkową konfigurację projektu Maven oraz kod szkieletowy aplikacji w katalogu registrations-codelab/. W sekcjach poniżej opisujemy zmiany w kodzie, które są niezbędne do utworzenia działającej aplikacji.
Edytor kodu Cloud Shell
Najprostszym sposobem na rozpoczęcie modyfikowania i wyświetlania kodu w środowisku Cloud Shell jest użycie wbudowanego edytora kodu Cloud Shell.
Po otwarciu instancji Cloud Shell kliknij ikonę ołówka, aby otworzyć edytor kodu. Edytor powinien umożliwiać bezpośrednie modyfikowanie plików projektu utworzonych przez Initialzr.

6. Konfiguracja bazy danych
Najpierw skonfiguruj aplikację, aby mogła łączyć się z bazą danych Cloud MySQL, którą skonfigurowano. Biblioteki Spring Cloud GCP oferują Cloud MySQL Starter, który zapewnia zależności niezbędne do łączenia się z instancją Cloud MySQL.
Dodaj zależność spring-cloud-gcp-starter-sql-mysql do pliku pom.xml projektu:
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>
Musisz też zmodyfikować plik konfiguracyjny application.properties, aby opisać konfigurację bazy danych. Skopiuj te właściwości do pliku application.properties.
Znajdź nazwę połączenia instancji z bazą danych:
$ gcloud sql instances describe codelab-instance \ --format 'value(connectionName)'
Dane wyjściowe tego polecenia zostaną użyte w pliku application.properties do skonfigurowania informacji o połączeniu.
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
Jedyną właściwością, którą musisz zmodyfikować, jest nazwa połączenia z instancją. Ta wartość musi być sformatowana jako wartość rozdzielona dwukropkiem w formacie YOUR_GCP_PROJECT_ID:REGION:DATABASE_INSTANCE_NAME.
7. Tworzenie treści statycznych
Najpierw utworzymy interfejs aplikacji. Aplikacja powinna zawierać formularz umożliwiający rejestrację osób oraz widok, w którym wyświetlają się wszystkie zarejestrowane osoby.
Na stronie głównej utwórz element index.html zawierający formularz rejestracji.
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>
Następnie utworzymy szablon Thymeleaf o nazwie registrants.html, który będzie wyświetlać zarejestrowanych użytkowników. Thymeleaf to platforma szablonów, której używamy do tworzenia i wyświetlania dynamicznie generowanego kodu HTML. Szablon wygląda jak HTML, ale zawiera dodatkowe elementy Markdown do obsługi treści dynamicznych. Ten szablon akceptuje jeden parametr o nazwie personsList, który zawiera wszystkich zarejestrowanych użytkowników.
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>
Na tym etapie możesz sprawdzić, czy wyświetlane są treści statyczne.
Skompiluj i uruchom aplikację za pomocą Maven:
$ ./mvnw spring-boot:run
Kliknij przycisk podglądu w oknie Cloud Shell i sprawdź, czy wyświetla się strona główna. Żadna funkcja interfejsu nie będzie działać, ponieważ nie mamy kontrolera internetowego. Zostanie on dodany w następnym kroku.

Po wyświetleniu podglądu aplikacji naciśnij CTRL+C, aby ją zamknąć.
8. Wysyłanie zarejestrowanych użytkowników do tematu Pub/Sub
W tym kroku wdrożymy funkcję, dzięki której dane rejestrujących przesłane za pomocą formularza internetowego będą publikowane w temacie Cloud Pub/Sub.
Dodawanie kategorii danych
Najpierw utworzymy klasy danych w języku Kotlin. Będą one naszymi encjami JPA i pośrednią reprezentacją osób rejestrujących się za pomocą formularza.
W pakiecie demonstracyjnym dodaj 2 nowe pliki: klasę Person i interfejs Spring Data PersonRepository. Te 2 klasy pozwolą nam łatwo przechowywać i pobierać wpisy rejestracyjne z bazy danych MySQL za pomocą 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>
Dodawanie kontrolera internetowego
Następnie utworzymy klasę Controller, która przetwarza dane rejestrujących z formularza i wysyła informacje do utworzonego wcześniej tematu Cloud Pub/Sub. Ten kontroler tworzy 2 punkty końcowe:
/registerPerson: punkt końcowy POST, w którym przesyłane są informacje o rejestrującym, a następnie wysyłane do tematu Pub/Sub. W funkcjiregisterPerson(..)informacje o rejestrującym są wysyłane do tematu Pub/Sub za pomocąPubSubTemplate, wygodnej klasy z integracji Spring Cloud GCP Pub/Sub, która minimalizuje powtarzalny kod potrzebny do rozpoczęcia interakcji z Cloud Pub/Sub./registrants: Wyświetla wszystkich zarejestrowanych użytkowników, którzy zostali pomyślnie zarejestrowani w bazie danych. Te informacje są pobierane z instancji MySQL za pomocą repozytorium Spring Data, które zostało utworzone w poprzednim kroku.
W pakiecie demonstracyjnym utwórz tę klasę kontrolera:
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))
}
}
Kontroler odczytuje informacje o rejestrującym przesłane za pomocą formularza internetowego, a następnie publikuje je w temacie Pub/Sub.
Dodawanie komponentu JSON Object Mapper Bean
W kontrolerze możesz zauważyć, że publikujemy w temacie Pub/Sub obiekt Person, a nie ciąg znaków. Jest to możliwe, ponieważ korzystamy z obsługi niestandardowych ładunków JSON w Spring Cloud GCP, które można wysyłać do tematów. Biblioteki umożliwiają serializowanie obiektów do formatu JSON, wysyłanie ładunków JSON do tematu i deserializowanie ładunku po jego otrzymaniu.
Aby korzystać z tej funkcji, musimy dodać do kontekstu aplikacji komponent ObjectMapper. Ten komponent ObjectMapper będzie używany do serializacji obiektów do formatu JSON i z niego, gdy aplikacja będzie wysyłać i odbierać wiadomości. W klasie DemoApplication.kt dodaj komponent 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)
}
W tym momencie możesz spróbować ponownie uruchomić aplikację, wpisując:
$ ./mvnw spring-boot:run
Formularz internetowy na stronie głównej będzie teraz wysyłać informacje do utworzonego przez Ciebie tematu Pub/Sub. Nie robi jednak jeszcze niczego przydatnego, ponieważ nadal musimy odczytywać dane z tego tematu Pub/Sub. Zrobisz to w następnym kroku.
9. Odczytywanie osób rejestrujących się z tematu Pub/Sub
W ostatnim kroku będziemy przetwarzać informacje o osobach rejestrujących się z tematu Pub/Sub i zapisywać je w bazie danych Cloud MySQL. Spowoduje to ukończenie aplikacji, co umożliwi przesyłanie nowych zgłoszeń rejestracyjnych za pomocą formularza i wyświetlanie wszystkich zarejestrowanych użytkowników za pomocą punktu końcowego /registrants.
Ta aplikacja będzie korzystać z Spring Integration, która oferuje wiele wygodnych abstrakcji do obsługi przesyłania wiadomości. Dodamy PubSubInboundChannelAdapter, aby umożliwić odczytywanie wiadomości z tematu Pub/Sub i umieszczanie ich w pubsubInputChannel w celu dalszego przetwarzania. Następnie skonfigurujemy funkcję messageReceiver za pomocą @ServiceActivator, aby była wywoływana w przypadku wiadomości docierających do 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)
}
W tym momencie konfiguracja aplikacji jest zakończona. Aby sprawdzić, czy aplikacja działa prawidłowo, uruchom:
$ ./mvnw spring-boot:run
Ponownie kliknij przycisk Podgląd i spróbuj zarejestrować użytkownika, wypełniając formularz i przesyłając go.

Kliknij link Zarejestrowane osoby, aby sprawdzić, czy nowy uczestnik jest widoczny w tabeli.

Gratulacje! Wszystko gotowe. Zatrzymaj aplikację, naciskając CTRL+C w oknie terminala.
10. Czyszczenie
Aby wyczyścić środowisko, musisz usunąć utworzony temat Pub/Sub i instancję Cloud MySQL.
Usuwanie instancji Cloud MySQL
$ gcloud sql instances delete codelab-instance
Usuwanie zasobów Pub/Sub
$ gcloud pubsub subscriptions delete registrations-sub $ gcloud pubsub topics delete registrations
11. Gratulacje!
Aplikacja Spring Kotlin, która integruje się z Cloud Pub/Sub i Cloud SQL (MySQL), została napisana.
Więcej informacji
- Projekt Spring on GCP: http://cloud.spring.io/spring-cloud-gcp/
- Repozytorium Spring on GCP w GitHubie: https://github.com/GoogleCloudPlatform/spring-cloud-gcp
- Java na Google Cloud Platform: https://cloud.google.com/java/
- Przykładowe aplikacje w Kotlinie korzystające z GCP: https://github.com/GoogleCloudPlatform/spring-cloud-gcp/tree/master/spring-cloud-gcp-kotlin-samples
Licencja
To zadanie jest licencjonowane na podstawie ogólnej licencji Creative Commons Attribution 2.0.