1. מבוא
Spring Framework 5.0 הוסיפה תמיכה ייעודית ב-Kotlin כדי להקל על מפתחי Kotlin להשתמש ב-Spring. המשמעות של השינויים האלה היא שהשילובים עם Google Cloud שמסופקים על ידי Spring Cloud GCP יפעלו בצורה חלקה ב-Kotlin. ב-Codelab הזה תראו כמה קל להתחיל להשתמש בשירותי Google Cloud באפליקציות של Kotlin!
ב-Codelab הזה תלמד להגדיר אפליקציה פשוטה לרישום ב-Kotlin לצורך שימוש בשירותי GCP, כולל Cloud Pub/Sub ו-Cloud SQL.
מה תפַתחו
ב-Codelab הזה, מגדירים אפליקציית Kotlin Spring Boot שמקבלת פרטים של רושם הדומיין, מפרסמת אותם בנושא Cloud Pub/Sub ושומרת אותם על מסד נתונים של Cloud MySQL.
מה תלמדו
איך משלבים את שירותי Google Cloud באפליקציית Kotlin Spring.
מה נדרש
- פרויקט ב-Google Cloud Platform
- דפדפן, למשל Chrome או Firefox
איך תשתמשו במדריך הזה?
איזה דירוג מגיע לחוויה שלך עם בניית אפליקציות אינטרנט מסוג HTML/CSS?
איזה דירוג מגיע לדעתך לחוויית השימוש בשירותי Google Cloud Platform?
2. הגדרה ודרישות
הגדרת סביבה בקצב עצמאי
- נכנסים למסוף Cloud ויוצרים פרויקט חדש או עושים שימוש חוזר בפרויקט קיים. (אם עדיין אין לכם חשבון Gmail או G Suite, עליכם ליצור חשבון).
חשוב לזכור את מזהה הפרויקט, שם ייחודי לכל הפרויקטים ב-Google Cloud (השם שלמעלה כבר תפוס ולא מתאים לכם, סליחה). בהמשך ב-Codelab הזה, היא תיקרא PROJECT_ID
.
- בשלב הבא צריך להפעיל את החיוב במסוף Cloud כדי להשתמש במשאבים של Google Cloud.
מעבר ב-Codelab הזה לא אמור לעלות הרבה, אם בכלל. חשוב לבצע את כל ההוראות בקטע 'ניקוי' שמסביר איך להשבית משאבים כדי שלא תצברו חיובים מעבר למדריך הזה. משתמשים חדשים ב-Google Cloud זכאים להשתתף בתוכנית תקופת ניסיון בחינם בשווי 1,200 ש"ח.
Google Cloud Shell
אומנם אפשר להפעיל את Google Cloud מרחוק מהמחשב הנייד, אבל ב-Codelab הזה נשתמש ב-Google Cloud Shell, סביבת שורת הפקודה שפועלת ב-Cloud.
הפעלת Cloud Shell
- במסוף Cloud, לוחצים על Activate Cloud Shell .
אם זו הפעם הראשונה שאתם מפעילים את Cloud Shell, יוצג לכם מסך ביניים (בחלק הנגלל) שמתאר מהו. במקרה כזה, לוחצים על המשך (וזה לא יקרה שוב). כך נראה המסך החד-פעמי:
ההקצאה וההתחברות ל-Cloud Shell נמשכת כמה דקות.
למכונה הווירטואלית הזו נטען כל כלי הפיתוח הדרושים. יש בה ספריית בית בנפח מתמיד של 5GB והיא פועלת ב-Google Cloud, מה שמשפר משמעותית את ביצועי הרשת והאימות. אם לא את כולן, ניתן לבצע חלק גדול מהעבודה ב-Codelab הזה באמצעות דפדפן או 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.
בטרמינל של Cloud Shell, מפעילים קודם את Pub/Sub API.
$ 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.
קודם כול, מפעילים את Cloud SQL Admin API.
$ 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. אתחול אפליקציה של 'אתחול קפיץ'
עכשיו אנחנו מוכנים להתחיל בכתיבת הבקשה. השלבים הבאים ימשיכו להשתמש ב-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 וגם קוד פיגוע (scaffling) לאפליקציה בספרייה registrations-codelab/
. בקטעים הבאים מתוארות פעולות העריכה בקוד שנדרשות כדי ליצור אפליקציה פעילה.
עורך קוד ב-Cloud Shell
הדרך הקלה ביותר להתחיל לשנות את הקוד ולצפות בו בסביבת Cloud Shell היא להשתמש ב-Cloud Shell Code Editor המובנה.
אחרי שפותחים מופע של Cloud Shell, לוחצים על סמל העיפרון כדי לפתוח את עורך הקוד. העורך צריך לאפשר לך לשנות ישירות את קובצי הפרויקט שהופקו על ידי Initialzr.
6. הגדרת מסד הנתונים
קודם כול, מגדירים את האפליקציה כך שהיא תוכל להתחבר למסד הנתונים ב-Cloud MySQL שהגדרתם. ספריות Spring Cloud GCP כוללות סימן לתחילת פעולה של Cloud MySQL, שמספק את יחסי התלות הנדרשים לחיבור למכונה של Cloud MySQL.
מוסיפים את התלות spring-cloud-gcp-starter-sql-mysql
לפרויקט pom.xml:
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>
בנוסף, עליך לשנות את קובץ התצורה 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, אבל היא כוללת כמה רכיבי Markdown נוספים שמשמשים לטיפול בתוכן דינמי. התבנית הזו מקבלת פרמטר יחיד בשם 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 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>
הוספת בקר האינטרנט
בשלב הבא ניצור מחלקה של נאמן מידע שתטפל בנרשמים מהטופס ותשלח את המידע לנושא Cloud Pub/Sub שיצרתם קודם. השלט רחוק הזה יוצר שתי נקודות קצה:
/registerPerson
: נקודת הקצה ב-POST שבה הפרטים של רושם הדומיין נשלחים לנושא Pub/Sub. בפונקציהregisterPerson(..)
, פרטי רושם הדומיין נשלחים לנושא Pub/Sub באמצעותPubSubTemplate
, סיווג נוחות משילובי Pub/Sub של Spring Cloud GCP שמצמצמים את הקוד הסטנדרטי שנדרש כדי להתחיל ליצור אינטראקציה עם Cloud Pub/Sub./registrants
: בעמודה הזו מוצגים כל רושם הדומיין שנרשמו בהצלחה במסד הנתונים. המידע הזה מאוחזר ממכונת MySQL באמצעות מאגר נתוני Spring שיצרנו בשלב הקודם.
יוצרים את מחלקה הבאה של נאמן מידע בחבילת ההדגמה:
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.
הוספת Bean של אובייקט JSON
יכול להיות שהבחנתם בנאמן המידע שאנחנו מפרסמים אובייקט Person
לנושא Pub/Sub ולא למחרוזת. זה מתאפשר כי אנחנו מנצלים את התמיכה של Spring Cloud GCP לשליחה של מטענים ייעודיים (payloads) מותאמים אישית של JSON לנושאים. הספריות מאפשרות לבצע סריאליזציה של אובייקטים ל-JSON, לשלוח מטענים ייעודיים של JSON לנושא, ולבצע פעולת deserialize של המטען הייעודי (payload) כשהוא מתקבל.
כדי ליהנות מהיתרונות של התכונה הזו, צריך להוסיף הייתה ObjectMapper
להקשר של האפליקציה שלך. ה-ObjectMapper
הזה ישמש ליצירת סריאליזציה של אובייקטים ל-JSON וממנו בזמן שהאפליקציה שולחת ומקבלת הודעות. בכיתה DemoApplication.kt
, מוסיפים את שעועית האביב 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).
מידע נוסף
- פרויקט לאביב ב-GCP: http://cloud.spring.io/spring-cloud-gcp/
- אביב במאגר GitHub של GCP: https://github.com/GoogleCloudPlatform/spring-cloud-gcp
- Java ב-Google Cloud Platform: 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 גנרי.