1. Введение
В Spring Framework 5.0 добавлена специальная поддержка Kotlin, упрощающая использование Spring разработчиками Kotlin. Следовательно, эти изменения означали, что интеграция с Google Cloud, предоставляемая Spring Cloud GCP, также без проблем работает в Kotlin. В этой лаборатории кода вы увидите, как легко начать использовать сервисы Google Cloud в своих приложениях Kotlin!
В этой лаборатории кода рассматривается настройка простого приложения регистрации на Kotlin, которое демонстрирует использование сервисов GCP, включая Cloud Pub/Sub и Cloud SQL .
Что ты построишь
В этой лаборатории кода вы настроите приложение Kotlin Spring Boot, которое принимает информацию о регистранте, публикует ее в теме Cloud Pub/Sub и сохраняет ее в базе данных Cloud MySQL.
Что вы узнаете
Как интегрироваться с сервисами Google Cloud в вашем приложении Kotlin Spring.
Что вам понадобится
- Проект облачной платформы Google
- Браузер, например Chrome или Firefox.
Как вы будете использовать это руководство?
Как бы вы оценили свой опыт создания веб-приложений HTML/CSS?
Как бы вы оценили свой опыт использования сервисов Google Cloud Platform?
2. Настройка и требования
Самостоятельная настройка среды
- Войдите в Cloud Console и создайте новый проект или повторно используйте существующий. (Если у вас еще нет учетной записи Gmail или G Suite, вам необходимо ее создать .)
Запомните идентификатор проекта — уникальное имя для всех проектов Google Cloud (имя, указанное выше, уже занято и не подойдет вам, извините!). Позже в этой лаборатории он будет называться PROJECT_ID
.
- Далее вам необходимо включить биллинг в Cloud Console, чтобы использовать ресурсы Google Cloud.
Прохождение этой лаборатории кода не должно стоить много, если вообще стоит. Обязательно следуйте всем инструкциям в разделе «Очистка», в которых рассказывается, как отключить ресурсы, чтобы вам не приходилось нести расходы, выходящие за рамки этого руководства. Новые пользователи Google Cloud имеют право на участие в программе бесплатной пробной версии стоимостью 300 долларов США .
Google Cloud Shell
Хотя Google Cloud можно управлять удаленно с вашего ноутбука, в этой лаборатории мы будем использовать Google Cloud Shell , среду командной строки, работающую в облаке.
Активировать Cloud Shell
- В Cloud Console нажмите «Активировать Cloud Shell». .
Если вы никогда раньше не запускали Cloud Shell, вам будет представлен промежуточный экран (ниже сгиба) с описанием того, что это такое. В этом случае нажмите «Продолжить» (и вы больше никогда его не увидите). Вот как выглядит этот одноразовый экран:
Подготовка и подключение к Cloud Shell займет всего несколько минут.
Эта виртуальная машина оснащена всеми необходимыми инструментами разработки. Он предлагает постоянный домашний каталог объемом 5 ГБ и работает в Google Cloud, что значительно повышает производительность сети и аутентификацию. Большую часть, если не всю, работу в этой лаборатории кода можно выполнить с помощью просто браузера или Chromebook.
После подключения к Cloud Shell вы увидите, что вы уже прошли аутентификацию и что для проекта уже установлен идентификатор вашего проекта.
- Выполните следующую команду в Cloud Shell, чтобы подтвердить, что вы прошли аутентификацию:
gcloud auth list
Вывод команды
Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com> To set the active account, run: $ gcloud config set account `ACCOUNT`
gcloud config list project
Вывод команды
[core] project = <PROJECT_ID>
Если это не так, вы можете установить это с помощью этой команды:
gcloud config set project <PROJECT_ID>
Вывод команды
Updated property [core/project].
3. Предоставление ресурсов Pub/Sub
Сначала нам нужно настроить тему и подписку Cloud Pub/Sub. В этом приложении мы будем публиковать регистрационную информацию в теме Pub/Sub; затем информация считывается из этого раздела и сохраняется в базе данных.
В этом руководстве мы будем полагаться на Cloud Shell для предоставления наших ресурсов. Обратите внимание, что ресурсы Pub/Sub также можно настроить через раздел Cloud Pub/Sub в Google Cloud Console.
В терминале Cloud Shell сначала включите API Pub/Sub.
$ gcloud services enable pubsub.googleapis.com
Далее мы создадим тему Pub/Sub с именем registrations
для этого приложения. Регистрационная информация, отправленная через приложение, будет опубликована в этой теме.
$ gcloud pubsub topics create registrations
Наконец, создайте подписку на эту тему. Подписка Pub/Sub позволяет вам получать сообщения по определенной теме.
$ gcloud pubsub subscriptions create registrations-sub --topic=registrations
Вы завершили создание темы Cloud Pub/Sub и подписки для своего приложения.
4. Создайте экземпляр Cloud SQL (MySQL) и базу данных.
Для нашего примера приложения нам также необходимо настроить экземпляр базы данных для хранения информации о регистранте. На этом этапе также будет использоваться терминал Cloud Shell для предоставления ресурсов Cloud SQL. Обратите внимание, что вы также можете просматривать и настраивать свои экземпляры Cloud SQL через Google Cloud Console .
Сначала включите API администратора Cloud SQL.
$ gcloud services enable sqladmin.googleapis.com
Далее мы предоставим экземпляр Cloud SQL (MySQL). Эта команда может занять некоторое время.
$ gcloud sql instances create codelab-instance --region=us-east1
После успешного создания экземпляра Cloud SQL создайте в нем новую базу данных под названием registrants
.
$ gcloud sql databases create registrants --instance codelab-instance
Вы завершили настройку экземпляра Cloud SQL и базы данных для своего приложения.
5. Инициализируйте приложение Spring Boot.
Теперь мы готовы приступить к написанию приложения. Следующие шаги будут продолжать использовать Cloud Shell, описанную в шагах по настройке.
Сначала мы будем использовать Initializr для генерации кода каркаса для проекта. В окне Cloud Shell выполните:
$ 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
Эта команда генерирует первоначальную настройку проекта Maven, а также код каркаса для вашего приложения в каталоге registrations-codelab/
. В следующих разделах описаны изменения кода, необходимые для создания работающего приложения.
Редактор кода Cloud Shell
Самый простой способ начать изменять и просматривать код в среде Cloud Shell — использовать встроенный редактор кода Cloud Shell .
Открыв экземпляр Cloud Shell, щелкните значок карандаша , чтобы открыть редактор кода. Редактор должен позволять вам напрямую изменять файлы проекта, созданные Initialzr.
6. Конфигурация базы данных
Сначала настройте свое приложение так, чтобы оно могло подключаться к настроенной вами базе данных Cloud MySQL. Библиотеки Spring Cloud GCP предлагают стартер Cloud MySQL , который предоставляет необходимые зависимости для подключения к экземпляру Cloud MySQL.
Добавьте зависимость spring-cloud-gcp-starter-sql-mysql
в проект pom.xml:
регистрации-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>
Кроме того, вам необходимо изменить файл конфигурации application.properties
, чтобы описать конфигурацию вашей базы данных. Скопируйте следующие свойства в файл application.properties
.
Найдите имя подключения экземпляра к вашей базе данных:
$ gcloud sql instances describe codelab-instance \ --format 'value(connectionName)'
Результаты этого будут использоваться в файле application.properties
для настройки информации о соединении.
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
Единственное свойство, которое вам необходимо изменить, — это имя соединения с экземпляром . Это значение должно быть отформатировано как значение, разделенное двоеточием, в следующем формате: YOUR_GCP_PROJECT_ID:REGION:DATABASE_INSTANCE_NAME
.
7. Создание статического контента
Сначала мы создадим интерфейс для нашего приложения. Приложение должно иметь форму, позволяющую кому-либо регистрировать физических лиц, а также представление, в котором отображаются все успешные зарегистрированные лица.
Для домашней страницы создайте файл index.html
, содержащий форму регистрации.
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>
Далее мы создадим шаблон Thymeleaf с именем registrants.html
для отображения зарегистрированных пользователей. Thymeleaf — это платформа шаблонов, которую мы используем для создания и обслуживания динамически создаваемого HTML. Вы увидите, что шаблон выглядит как HTML, за исключением некоторых дополнительных элементов уценки для обработки динамического контента. Этот шаблон принимает один параметр с именем personsList
, который содержит всех владельцев регистрации, зарегистрированных через приложение.
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>
На этом этапе вы можете убедиться, что статический контент обслуживается.
Создайте и запустите приложение с помощью Maven:
$ ./mvnw spring-boot:run
Нажмите кнопку предварительного просмотра в окне Cloud Shell и убедитесь, что вы видите отображаемую домашнюю страницу. Однако ни одна из функций пользовательского интерфейса не будет работать, поскольку нам не хватает веб-контроллера. Это будет добавлено на следующем этапе.
После предварительного просмотра приложения нажмите CTRL+C
чтобы закрыть приложение.
8. Отправка зарегистрировавшихся в тему Pub/Sub
На этом этапе мы реализуем функцию, при которой зарегистрированные лица, отправленные через веб-форму, будут публиковаться в теме Cloud Pub/Sub.
Добавьте классы данных
Сначала мы создадим несколько классов данных Kotlin; это будут наши организации JPA, а также они будут выступать в качестве нашего промежуточного представителя зарегистрированных лиц, отправленных через форму.
В демонстрационный пакет добавьте два новых файла: класс Person
и Spring Data PersonRepository
. Эти два класса позволят нам легко хранить и извлекать регистрационные записи из нашей базы данных MySQL с помощью 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>
Добавьте веб-контроллер
Далее мы создадим класс Controller, который обрабатывает регистрантов из формы и отправляет информацию в тему Cloud Pub/Sub, созданную вами ранее. Этот контроллер создает две конечные точки:
-
/registerPerson
: конечная точка POST, в которую передается информация о регистранте, а затем отправляется в тему Pub/Sub. В функцииregisterPerson(..)
информация о регистранте отправляется в тему Pub/Sub с помощьюPubSubTemplate
, удобного класса из интеграции Spring Cloud GCP Pub/Sub, который сводит к минимуму шаблонный код, необходимый для начала взаимодействия с Cloud Pub/Sub. -
/registrants
: отображает всех зарегистрированных лиц, успешно зарегистрированных в базе данных. Эта информация извлекается из экземпляра MySQL с использованием репозитория Spring Data, который мы создали на предыдущем шаге.
Создайте следующий класс контроллера в демонстрационном пакете:
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))
}
}
Контроллер считывает информацию о регистранте, отправленную через веб-форму, а затем публикует эту информацию в теме Pub/Sub.
Добавление компонента JSON Object Mapper
Возможно, вы заметили, что в контроллере мы публикуем объект Person
в теме Pub/Sub, а не строку. Это возможно, поскольку мы используем поддержку Spring Cloud GCP для отправки пользовательских полезных данных JSON в темы — библиотеки позволяют сериализовать объекты в JSON , отправлять полезные данные JSON в тему и десериализовать полезные данные при их получении.
Чтобы воспользоваться этой функцией, мы должны добавить компонент ObjectMapper
в контекст вашего приложения. Этот объект ObjectMapper
будет использоваться для сериализации объектов в JSON и обратно, когда ваше приложение отправляет и получает сообщения. В класс DemoApplication.kt
добавьте 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)
}
На этом этапе вы можете попробовать запустить приложение еще раз, выполнив:
$ ./mvnw spring-boot:run
Из веб-формы на главной странице приложение теперь будет отправлять информацию в созданную вами тему Pub/Sub. Однако он по-прежнему не делает ничего полезного, потому что нам все еще нужно читать эту тему Pub/Sub! Это достигается на следующем этапе.
9. Чтение зарегистрированных пользователей из темы Pub/Sub
На последнем этапе мы обработаем информацию о регистранте из темы Pub/Sub и сохраним ее в базе данных Cloud MySQL. На этом заявка будет завершена, и вы сможете отправлять новых регистрантов через форму и просматривать всех зарегистрированных пользователей через конечную точку /registrants
.
Это приложение будет использовать преимущества Spring Integration , предлагающие множество удобных абстракций для работы с сообщениями. Мы добавим PubSubInboundChannelAdapter
, чтобы мы могли читать сообщения из темы Pub/Sub и помещать их в pubsubInputChannel
для дальнейшей обработки. Затем мы настроим функцию messageReceiver
с помощью @ServiceActivator
для вызова сообщений, поступающих на 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)
}
На этом этапе вы завершили настройку приложения. Чтобы убедиться, что приложение работает правильно, запустите:
$ ./mvnw spring-boot:run
Нажмите кнопку «Предварительный просмотр» еще раз и попробуйте зарегистрировать пользователя, заполнив форму и отправив ее.
Нажмите ссылку «Зарегистрированные люди» , чтобы убедиться, что новый владелец регистрации появился в таблице.
Поздравляем, все готово! Завершите работу приложения, нажав CTRL+C
в окне терминала.
10. Очистка
Чтобы очистить среду, вам необходимо удалить тему Pub/Sub и созданный вами экземпляр Cloud MySQL.
Удаление экземпляра Cloud MySQL
$ gcloud sql instances delete codelab-instance
Удаление ресурсов Pub/Sub
$ gcloud pubsub subscriptions delete registrations-sub $ gcloud pubsub topics delete registrations
11. Поздравляем!
Вы завершили написание приложения Spring Kotlin, которое интегрируется с Cloud Pub/Sub и Cloud SQL (MySQL).
Узнать больше
- Spring в проекте GCP: http://cloud.spring.io/spring-cloud-gcp/
- Spring в репозитории GCP GitHub: https://github.com/GoogleCloudPlatform/spring-cloud-gcp
- Java на облачной платформе Google: https://cloud.google.com/java/
- Примеры приложений Kotlin с использованием GCP: https://github.com/GoogleCloudPlatform/spring-cloud-gcp/tree/master/spring-cloud-gcp-kotlin-samples .
Лицензия
Эта работа доступна под лицензией Creative Commons Attribution 2.0 Generic License.