1. Wprowadzenie
Platforma Spring Framework 5.0 dodała specjalną obsługę Kotlin, ułatwiając deweloperom Kotlin korzystanie z platformy Spring. W efekcie zmiany te sprawiły, że integracje z Google Cloud dostępne w ramach usługi Spring Cloud GCP również bezproblemowo działają w kotlinie. Dzięki temu ćwiczeniu w Codelabs dowiesz się, jak łatwo jest zacząć korzystać z usług Google Cloud w aplikacjach Kotlin.
Dzięki nim dowiesz się, jak skonfigurować prostą aplikację do rejestracji w Kotlin, która pokazuje korzystanie z usług GCP, takich jak Cloud Pub/Sub i Cloud SQL.
Co utworzysz
W ramach tego ćwiczenia w programie skonfigurujesz aplikację Kotlin Spring Boot, która przyjmuje informacje o abonentach, opublikujesz ją w temacie Cloud Pub/Sub i utrwala w bazie danych Cloud MySQL.
Czego się nauczysz
Integracja z usługami Google Cloud w aplikacji Kotlin Spring.
Czego potrzebujesz
- Projekt Google Cloud Platform
- Przeglądarka, np. Chrome lub Firefox
Jak wykorzystasz ten samouczek?
Jak oceniasz swoje doświadczenie z tworzeniem aplikacji internetowych HTML/CSS?
Jak oceniasz swoje wrażenia z korzystania 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 wykorzystaj już istniejący. Jeśli nie masz jeszcze konta Gmail lub G Suite, musisz je utworzyć.
Zapamiętaj identyfikator projektu, unikalną nazwę we wszystkich projektach Google Cloud (powyższa nazwa jest już zajęta i nie będzie Ci odpowiadać). W dalszej części tego ćwiczenia w programie będzie ona określana jako PROJECT_ID
.
- Następnie musisz włączyć płatności w Cloud Console, aby korzystać z zasobów Google Cloud.
Ukończenie tego ćwiczenia z programowania nie powinno kosztować zbyt wiele. Postępuj zgodnie z instrukcjami podanymi w sekcji „Czyszczenie” W tym samouczku znajdziesz wskazówki, jak wyłączyć zasoby, aby uniknąć naliczania opłat. Nowi użytkownicy Google Cloud mogą skorzystać z programu bezpłatnego okresu próbnego o wartości 300 USD.
Google Cloud Shell,
Google Cloud można obsługiwać zdalnie z Twojego laptopa, ale w ramach tego ćwiczenia z programowania wykorzystamy Google Cloud Shell – środowisko wiersza poleceń działające w chmurze.
Aktywowanie Cloud Shell
- W konsoli Cloud kliknij Aktywuj Cloud Shell .
Jeśli dopiero zaczynasz korzystać z Cloud Shell, wyświetli się ekran pośredni (w części strony widocznej po przewinięciu) z opisem tej funkcji. W takim przypadku kliknij Dalej (nie zobaczysz go więcej). Tak wygląda ten jednorazowy ekran:
Uzyskanie dostępu do Cloud Shell i połączenie się z nim powinno zająć tylko kilka chwil.
Ta maszyna wirtualna ma wszystkie potrzebne narzędzia dla programistów. Zawiera stały katalog domowy o pojemności 5 GB i działa w Google Cloud, co znacznie zwiększa wydajność sieci i uwierzytelnianie. Większość czynności z tego ćwiczenia z programowania można wykonać w przeglądarce lub na Chromebooku.
Po nawiązaniu połączenia z Cloud Shell powinno pojawić się potwierdzenie, że użytkownik jest już uwierzytelniony i że projekt jest już ustawiony na identyfikator Twojego projektu.
- Uruchom to polecenie w Cloud Shell, aby potwierdzić, że jesteś uwierzytelniony:
gcloud auth list
Dane wyjściowe 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
Dane wyjściowe polecenia
[core] project = <PROJECT_ID>
Jeśli tak nie jest, możesz go ustawić za pomocą tego polecenia:
gcloud config set project <PROJECT_ID>
Dane wyjściowe polecenia
Updated property [core/project].
3. Udostępnianie zasobów Pub/Sub
Najpierw musimy skonfigurować temat i subskrypcję Cloud Pub/Sub. W tej aplikacji opublikujemy informacje rejestracyjne w temacie Pub/Sub. informacje są następnie odczytywane z tego tematu i zapisywane w bazie danych.
W tym samouczku będziemy udostępniać nasze zasoby za pomocą Cloud Shell. Pamiętaj, że zasoby Pub/Sub można też konfigurować 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ą zgłoszenia zostaną opublikowane w tym temacie.
$ gcloud pubsub topics create registrations
Na koniec utwórz subskrypcję dotyczącą tego tematu. Subskrypcja Pub/Sub umożliwia odbieranie wiadomości z tematu.
$ gcloud pubsub subscriptions create registrations-sub --topic=registrations
Udało Ci się utworzyć temat i subskrypcję Cloud Pub/Sub dla Twojej aplikacji.
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 abonencie. Ten krok będzie także zależał od terminala Cloud Shell do obsługi administracyjnej zasobów Cloud SQL. Pamiętaj, że instancje Cloud SQL możesz wyświetlać i konfigurować w konsoli Google Cloud.
Najpierw włącz Cloud SQL Admin API.
$ gcloud services enable sqladmin.googleapis.com
Następnie udostępnimy instancję Cloud SQL (MySQL). Wykonanie polecenia może zająć trochę czasu.
$ 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
Masz już za sobą konfigurowanie instancji i bazy danych Cloud SQL dla Twojej aplikacji.
5. Inicjowanie aplikacji Spring Boot
Teraz możemy zacząć pisać zgłoszenie. Kolejne kroki będą kontynuowane przy użyciu Cloud Shell opisanej w krokach konfiguracji.
Najpierw użyjemy narzędzia Initializr, aby wygenerować kod rusztowania dla projektu. W oknie Cloud Shell uruchom polecenie:
$ 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 wstępną konfigurację projektu Maven oraz kod rusztowania dla aplikacji w katalogu registrations-codelab/
. W kolejnych sekcjach opisano zmiany w kodzie niezbędne do utworzenia działającej aplikacji.
Edytujący kod Cloud Shell
Najprostszym sposobem rozpoczęcia 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 pozwalać na bezpośrednie modyfikowanie plików projektu utworzonych przez Initialzr.
6. Konfiguracja bazy danych
Najpierw skonfiguruj aplikację tak, aby mogła łączyć się ze skonfigurowaną bazą danych Cloud MySQL. Biblioteki Spring Cloud GCP oferują pole inicjujące Cloud MySQL, które określa zależności niezbędne do nawiązania połączenia 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>
Dodatkowo musisz zmodyfikować plik konfiguracji 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 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 instancji. Ta wartość musi być sformatowana jako wartość rozdzielana dwukropkiem w formacie YOUR_GCP_PROJECT_ID:REGION:DATABASE_INSTANCE_NAME
.
7. Tworzenie treści statycznej
Najpierw utworzymy frontend naszej aplikacji. Zgłoszenie powinno zawierać formularz umożliwiający rejestrację osób oraz widok z listą wszystkich zarejestrowanych użytkowników.
Dla strony głównej utwórz index.html
zawierający formularz rejestracyjny.
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
do wyświetlania zarejestrowanych użytkowników. Thymeleaf to struktura szablonów, z których korzystamy do tworzenia i wyświetlania dynamicznie tworzonego kodu HTML. Szablon wygląda jak HTML, ale zawiera dodatkowe elementy Markdown do obsługi zawartości dynamicznej. Ten szablon akceptuje jeden parametr o nazwie personsList
, który zawiera wszystkich abonentów, którzy zostali zarejestrowani za pomocą aplikacji.
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ę, korzystając z narzędzia Maven:
$ ./mvnw spring-boot:run
Kliknij przycisk podglądu w oknie Cloud Shell i sprawdź, czy widzisz renderowaną stronę główną. Żadna z funkcji interfejsu nie będzie jednak działać, ponieważ brakuje kontrolera internetowego. Zostanie ona dodana 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 rejestratorzy, którzy przesłali formularz za pomocą formularza internetowego, będą opublikowani w temacie Cloud Pub/Sub.
Dodaj klasy danych
Najpierw utworzymy klasy danych Kotlin, będą to podmioty w ramach JPA i będą także pośredniczyć w reprezentowaniu abonentów przesłanych za pomocą formularza.
W pakiecie demonstracyjnym dodaj 2 nowe pliki: klasę Person
i Spring Data PersonRepository
. Te 2 klasy pozwolą nam łatwo przechowywać i pobierać wpisy rejestracyjne z bazy danych MySQL przy użyciu 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ę administratora, która przetwarza abonentów 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 są przesyłane informacje o abonencie, a następnie wysyłane do tematu Pub/Sub. W funkcjiregisterPerson(..)
informacje o abonencie są wysyłane do tematu Pub/Sub za pomocąPubSubTemplate
, czyli 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 w bazie danych. Te informacje są pobierane z instancji MySQL przy użyciu repozytorium Spring Data utworzonego w poprzednim kroku.
Utwórz tę klasę kontrolera w pakiecie demonstracyjnym:
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))
}
}
Administrator odczytuje informacje o abonentach przesłane za pomocą formularza internetowego, a następnie publikuje je w temacie Pub/Sub.
Dodawanie komponentu JSON Object Mapper
W kontrolerze możesz zauważyć, że w temacie Pub/Sub publikujemy obiekt Person
, a nie ciąg znaków. Jest to możliwe, ponieważ wykorzystujemy obsługę Spring Cloud GCP do wysyłania niestandardowych ładunków JSON do tematów. Biblioteki umożliwiają serializację obiektów w formacie JSON, wysyłanie ładunków JSON do tematu i deserializację ładunku po jego otrzymaniu.
Aby korzystać z tej funkcji, musimy dodać do kontekstu Twojej aplikacji komponent bean ObjectMapper
. Ten obiekt typu bean ObjectMapper
będzie używany do serializowania obiektów do i z JSON, gdy aplikacja wysyła i odbiera wiadomości. Do zajęć DemoApplication.kt
dodaj ziarna wiosenna 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)
}
Na tym etapie możesz spróbować ponownie uruchomić aplikację, uruchamiając polecenie:
$ ./mvnw spring-boot:run
Z formularza internetowego na stronie głównej aplikacja wyśle informacje do utworzonego tematu Pub/Sub. Jednak nadal nie jest on jeszcze przydatny, ponieważ nadal musimy czytać z tego tematu Pub/Sub. Zostanie to zrobione w następnym kroku.
9. Odczytywanie rejestrujących się z tematu Pub/Sub
W ostatnim kroku przetworzymy informacje o abonentach z tematu Pub/Sub i zachujemy je w bazie danych Cloud MySQL. Spowoduje to zakończenie zgłoszenia, co umożliwi Ci przesłanie nowych zarejestrowanych użytkowników za pomocą formularza i wyświetlenie wszystkich zarejestrowanych użytkowników za pomocą punktu końcowego /registrants
.
Ta aplikacja będzie korzystać z usługi Spring Integration, która umożliwia wygodne obchodzenie się z wiadomościami. Dodamy obiekt PubSubInboundChannelAdapter
, aby umożliwić nam odczytywanie wiadomości z tematu Pub/Sub i umieszczanie ich w elemencie pubsubInputChannel
w celu dalszego przetwarzania. Następnie skonfigurujemy funkcję messageReceiver
za pomocą metody @ServiceActivator
, która będzie wywoływana z wiadomościami, które dotarły 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)
}
Na tym etapie konfiguracja aplikacji została ukończona. Aby sprawdzić, czy aplikacja działa prawidłowo, uruchom polecenie:
$ ./mvnw spring-boot:run
Ponownie kliknij przycisk Podgląd i spróbuj zarejestrować użytkownika, wypełniając i przesyłając formularz.
Kliknij link Zarejestrowane osoby, aby sprawdzić, czy nowy abonent wyświetla się w tabeli.
Gratulacje, to już wszystko! Zakończ aplikację, naciskając CTRL+C
w oknie terminala.
10. Czyszczenie
Aby wyczyścić środowisko, musisz usunąć temat Pub/Sub i utworzoną 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!
Udało Ci się napisać aplikację Spring Kotlin, która integruje się z Cloud Pub/Sub i Cloud SQL (MySQL).
Więcej informacji
- Projekt wiosenny w GCP: http://cloud.spring.io/spring-cloud-gcp/
- Repozytorium Spring on GCP na GitHubie: https://github.com/GoogleCloudPlatform/spring-cloud-gcp
- Java w Google Cloud Platform: https://cloud.google.com/java/
- Przykładowe aplikacje Kotlin 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.