1. ภาพรวม
ในโค้ดแล็บนี้ เราจะได้เรียนรู้เกี่ยวกับโปรเจ็กต์ Spring Native, การสร้างแอปที่ใช้โปรเจ็กต์ดังกล่าว และทำให้ใช้งานได้ใน Google Cloud
เราจะพูดถึงคอมโพเนนต์ ประวัติล่าสุดของโปรเจ็กต์ กรณีการใช้งานบางส่วน และแน่นอนขั้นตอนที่จำเป็นสำหรับคุณในการใช้โปรเจ็กต์ในโปรเจ็กต์ของคุณ
ขณะนี้โปรเจ็กต์ Spring Native อยู่ในขั้นทดลอง จึงต้องมีการกำหนดค่าบางอย่างเพื่อเริ่มต้นใช้งาน อย่างไรก็ตาม ตามที่ได้ประกาศไว้ในงาน SpringOne 2021 ทางเราจะผสานรวม Spring Native เข้ากับ Spring Framework 6.0 และ Spring Boot 3.0 พร้อมการสนับสนุนชั้นยอด ดังนั้นตอนนี้จึงเป็นโอกาสที่ดีที่จะได้ศึกษาโปรเจ็กต์นี้อย่างละเอียดอีกสัก 2-3 เดือนก่อนเปิดตัว
แม้ว่าการคอมไพล์แบบทันเวลาจะได้รับการเพิ่มประสิทธิภาพอย่างดีสำหรับสิ่งต่างๆ เช่น กระบวนการที่ทำงานเป็นเวลานาน แต่ก็มีบางกรณีการใช้งานที่แอปพลิเคชันที่คอมไพล์ล่วงหน้าจะมีประสิทธิภาพดีกว่า ซึ่งเราจะพูดถึงในโค้ดแล็บ
คุณจะได้เรียนรู้วิธีต่อไปนี้
- ใช้ Cloud Shell
- เปิดใช้ Cloud Run API
- สร้างและทำให้แอป Spring Native ใช้งานได้
- ทำให้แอปดังกล่าวใช้งานได้ใน Cloud Run
สิ่งที่ต้องมี
- โปรเจ็กต์ Google Cloud Platform ที่มีบัญชีสำหรับการเรียกเก็บเงิน GCP ที่ใช้งานอยู่
- ติดตั้ง gcloud cli หรือมีสิทธิ์เข้าถึง Cloud Shell
- ทักษะพื้นฐานด้าน Java + XML
- ความรู้เกี่ยวกับคำสั่งทั่วไปของ Linux
แบบสำรวจ
คุณจะใช้บทแนะนำนี้อย่างไร
คุณจะให้คะแนนประสบการณ์การใช้งาน Java เท่าไร
คุณจะให้คะแนนประสบการณ์การใช้งานบริการ Google Cloud เท่าใด
2. ข้อมูลเบื้องต้น
โปรเจ็กต์ Spring Native ใช้เทคโนโลยีหลายอย่างเพื่อมอบประสิทธิภาพของแอปพลิเคชันเนทีฟให้แก่นักพัฒนาแอป
หากต้องการทําความเข้าใจ Spring Native อย่างเต็มรูปแบบ คุณควรทําความเข้าใจเทคโนโลยีคอมโพเนนต์เหล่านี้ 2-3 อย่าง ประโยชน์ที่เทคโนโลยีเหล่านี้มอบให้เรา และวิธีทํางานร่วมกัน
การคอมไพล์ AOT
เมื่อนักพัฒนาซอฟต์แวร์เรียกใช้ javac ตามปกติในเวลาคอมไพล์ ซอร์สโค้ด .java ของเราจะคอมไพล์เป็นไฟล์ .class ซึ่งเขียนด้วยไบต์โค้ด บิตโค้ดนี้มีไว้เพื่อให้เครื่องเสมือน Java เข้าใจเท่านั้น ดังนั้น JVM จะต้องตีความโค้ดนี้บนเครื่องอื่นๆ เพื่อให้เราเรียกใช้โค้ดได้
กระบวนการนี้ช่วยให้เรานําโค้ด Java ไปใช้ได้ในทุกแพลตฟอร์ม ซึ่งช่วยให้เรา "เขียนครั้งเดียวและใช้งานได้ทุกที่" แต่ก็มีค่าใช้จ่ายสูงเมื่อเทียบกับการเรียกใช้โค้ดเนทีฟ
แต่โชคดีที่การใช้งาน JVM ส่วนใหญ่ใช้การคอมไพล์แบบทันท่วงทีเพื่อลดต้นทุนการตีความนี้ ซึ่งทำได้โดยการนับการเรียกใช้ฟังก์ชัน และหากมีการเรียกใช้บ่อยพอที่จะผ่านเกณฑ์ ( 10,000 ครั้งโดยค่าเริ่มต้น) ระบบจะคอมไพล์เป็นโค้ดเนทีฟที่รันไทม์เพื่อป้องกันการตีความที่สิ้นเปลืองค่าใช้จ่ายเพิ่มเติม
การคอมไพล์ล่วงหน้าใช้แนวทางที่ตรงกันข้าม โดยคอมไพล์โค้ดที่เข้าถึงได้ทั้งหมดเป็นไฟล์ปฏิบัติการแบบเนทีฟ ณ เวลาที่คอมไพล์ ซึ่งจะแลกความสามารถในการพกพาเพื่อประสิทธิภาพหน่วยความจําและประสิทธิภาพอื่นๆ ที่เพิ่มขึ้นเมื่อรันไทม์
แน่นอนว่าข้อเสียนี้อาจไม่คุ้มค่าเสมอไป อย่างไรก็ตาม การคอมไพล์ AOT จะมีประโยชน์ในบางกรณี เช่น
- แอปพลิเคชันที่มีการใช้งานสั้นๆ ซึ่งเวลาเริ่มต้นมีความสำคัญ
- สภาพแวดล้อมที่มีหน่วยความจําจํากัดสูงซึ่ง JIT อาจใช้ทรัพยากรมากเกินไป
เกร็ดความรู้ที่น่าสนใจคือ การคอมไพล์ AOT เปิดตัวเป็นฟีเจอร์ทดลองใน JDK 9 แม้ว่าการใช้งานนี้จะบำรุงรักษาได้ยากและไม่ค่อยได้รับความนิยมนัก แต่เราก็นําออกอย่างเงียบๆ ใน Java 17 เพื่อสนับสนุนนักพัฒนาซอฟต์แวร์ที่ใช้ GraalVM เท่านั้น
GraalVM
GraalVM เป็น JDK แบบโอเพนซอร์สที่ได้รับการเพิ่มประสิทธิภาพอย่างสูง ซึ่งใช้เวลาเริ่มต้นอย่างรวดเร็วมาก มีการคอมไพล์อิมเมจเนทีฟ AOT และความสามารถในการใช้หลายภาษา ซึ่งช่วยให้นักพัฒนาแอปสามารถผสมผสานหลายภาษาไว้ในแอปพลิเคชันเดียวได้
GraalVM อยู่ระหว่างการพัฒนาอย่างต่อเนื่อง โดยได้รับความสามารถใหม่ๆ และปรับปรุงความสามารถที่มีอยู่อยู่เสมอ เราจึงขอแนะนำให้นักพัฒนาซอฟต์แวร์ติดตามข่าวสารอยู่เสมอ
เหตุการณ์สำคัญล่าสุดบางส่วนมีดังนี้
- เอาต์พุตการสร้างรูปภาพเนทีฟใหม่ที่ใช้งานง่าย ( 2021-01-18)
- การรองรับ Java 17 ( 2022-01-18)
- การเปิดใช้การคอมไพล์แบบหลายระดับโดยค่าเริ่มต้นเพื่อปรับปรุงเวลาในการคอมไพล์หลายภาษา ( 2021-04-20)
Spring Native
กล่าวอย่างง่ายคือ Spring Native ช่วยให้ใช้คอมไพเลอร์รูปภาพเนทีฟของ GraalVM เพื่อเปลี่ยนแอปพลิเคชัน Spring ให้เป็นไฟล์ปฏิบัติการแบบเนทีฟได้
กระบวนการนี้เกี่ยวข้องกับการวิเคราะห์แบบคงที่ของแอปพลิเคชัน ณ เวลาคอมไพล์เพื่อค้นหาเมธอดทั้งหมดในแอปพลิเคชันซึ่งเข้าถึงได้จากจุดแรกเข้า
ซึ่งโดยพื้นฐานแล้วจะเป็นการสร้างแนวคิด "โลกแบบปิด" ของแอปพลิเคชัน โดยระบบจะถือว่าโค้ดทั้งหมดเป็นที่รู้จัก ณ เวลาคอมไพล์ และจะไม่อนุญาตให้โหลดโค้ดใหม่เมื่อรันไทม์
โปรดทราบว่าการสร้างรูปภาพเนทีฟเป็นกระบวนการที่ใช้หน่วยความจำมาก ซึ่งใช้เวลานานกว่าการคอมไพล์แอปพลิเคชันปกติ และมีข้อจํากัดบางประการในบางแง่มุมของ Java
ในบางกรณี คุณไม่จําเป็นต้องเปลี่ยนแปลงโค้ดเพื่อให้แอปพลิเคชันทํางานร่วมกับ Spring Native ได้ อย่างไรก็ตาม ในบางสถานการณ์ต้องมีการกําหนดค่าเนทีฟที่เฉพาะเจาะจงจึงจะทํางานได้อย่างถูกต้อง ในกรณีเหล่านี้ Spring Native มักจะให้คำแนะนำแบบเนทีฟเพื่อลดความซับซ้อนของกระบวนการนี้
3. การตั้งค่า/การเตรียมงาน
ก่อนเริ่มใช้งาน Spring Native เราจะต้องสร้างและติดตั้งใช้งานแอปเพื่อสร้างพื้นฐานประสิทธิภาพที่เรานำไปเปรียบเทียบกับเวอร์ชันเนทีฟในภายหลังได้
1. การสร้างโปรเจ็กต์
เราจะเริ่มต้นด้วยการสร้างแอปจาก start.spring.io โดยทำดังนี้
curl https://start.spring.io/starter.zip -d dependencies=web \ -d javaVersion=11 \ -d bootVersion=2.6.4 -o io-native-starter.zip
แอปเริ่มต้นนี้ใช้ Spring Boot 2.6.4 ซึ่งเป็นเวอร์ชันล่าสุดที่โปรเจ็กต์ spring-native รองรับ ณ เวลาที่เขียน
โปรดทราบว่าตั้งแต่การเปิดตัว GraalVM 21.0.3 คุณสามารถใช้ Java 17 กับตัวอย่างนี้ได้เช่นกัน เราจะยังคงใช้ Java 11 ในบทแนะนำนี้เพื่อลดการกำหนดค่าที่เกี่ยวข้อง
เมื่อเรามีไฟล์ ZIP ในบรรทัดคำสั่งแล้ว เราก็สามารถสร้างไดเรกทอรีย่อยสำหรับโปรเจ็กต์และแตกไฟล์โฟลเดอร์ในนั้นได้โดยทำดังนี้
mkdir spring-native cd spring-native unzip ../io-native-starter.zip
2. การเปลี่ยนแปลงโค้ด
เมื่อเปิดโปรเจ็กต์แล้ว เราจะเพิ่มสัญญาณของชีวิตอย่างรวดเร็วและแสดงประสิทธิภาพของ Spring Native เมื่อเรียกใช้
แก้ไข DemoApplication.java ให้ตรงกับข้อมูลต่อไปนี้
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.Duration;
import java.time.Instant;
@RestController
@SpringBootApplication
public class DemoApplication {
private static Instant startTime;
private static Instant readyTime;
public static void main(String[] args) {
startTime = Instant.now();
SpringApplication.run(DemoApplication.class, args);
}
@GetMapping("/")
public String index() {
return "Time between start and ApplicationReadyEvent: "
+ Duration.between(startTime, readyTime).toMillis()
+ "ms";
}
@EventListener(ApplicationReadyEvent.class)
public void ready() {
readyTime = Instant.now();
}
}
ตอนนี้แอปพื้นฐานของเราพร้อมใช้งานแล้ว คุณจึงสร้างภาพและเรียกใช้ในเครื่องเพื่อดูเวลาเริ่มต้นได้ก่อนที่จะแปลงเป็นแอปพลิเคชันเนทีฟ
วิธีสร้างรูปภาพ
mvn spring-boot:build-image
นอกจากนี้ คุณยังใช้ docker images demo
เพื่อดูขนาดของรูปภาพพื้นฐานได้
วิธีเรียกใช้แอป
docker run --rm -p 8080:8080 demo:0.0.1-SNAPSHOT
3. ติดตั้งใช้งานแอปพื้นฐาน
ตอนนี้เรามีแอปแล้ว เราจะติดตั้งใช้งานและจดบันทึกเวลาไว้เพื่อเปรียบเทียบกับเวลาเริ่มต้นของแอปเนทีฟในภายหลัง
บริการโฮสติ้งข้อมูลของคุณมีหลายประเภท ขึ้นอยู่กับประเภทแอปพลิเคชันที่คุณสร้าง
อย่างไรก็ตาม เนื่องจากตัวอย่างของเราเป็นเว็บแอปพลิเคชันที่เรียบง่ายและตรงไปตรงมา เราจึงใช้ Cloud Run ได้อย่างง่ายดาย
หากคุณทำตามในเครื่องของคุณเอง โปรดตรวจสอบว่าได้ติดตั้งและอัปเดตเครื่องมือ gcloud CLI แล้ว
หากคุณใช้ Cloud Shell ระบบจะจัดการทุกอย่างให้คุณและคุณเพียงเรียกใช้คำสั่งต่อไปนี้ในไดเรกทอรีต้นทาง
gcloud run deploy
4. การกำหนดค่าแอปพลิเคชัน
1. การกำหนดค่าที่เก็บ Maven
เนื่องจากโปรเจ็กต์นี้ยังอยู่ในขั้นทดลอง เราจึงต้องกำหนดค่าแอปเพื่อให้ค้นหาอาร์ติแฟกต์เวอร์ชันทดลองได้ ซึ่งไม่มีอยู่ในที่เก็บข้อมูลส่วนกลางของ Maven
ซึ่งจะเกี่ยวข้องกับการเพิ่มองค์ประกอบต่อไปนี้ลงใน pom.xml ซึ่งคุณทําได้ในเครื่องมือแก้ไขที่เลือก
เพิ่มส่วน repositories และ pluginRepositories ต่อไปนี้ลงใน pom
<repositories>
<repository>
<id>spring-release</id>
<name>Spring release</name>
<url>https://repo.spring.io/release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-release</id>
<name>Spring release</name>
<url>https://repo.spring.io/release</url>
</pluginRepository>
</pluginRepositories>
2. การเพิ่มทรัพยากร Dependency
ถัดไป ให้เพิ่มข้อกําหนดของ spring-native ซึ่งจําเป็นสําหรับเรียกใช้แอปพลิเคชัน Spring เป็นอิมเมจเนทีฟ หมายเหตุ: ขั้นตอนนี้ไม่จําเป็นหากคุณใช้ Gradle
<dependencies>
<!-- ... -->
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-native</artifactId>
<version>0.11.2</version>
</dependency>
</dependencies>
3. การเพิ่ม/เปิดใช้ปลั๊กอิน
จากนั้นเพิ่มปลั๊กอิน AOT เพื่อปรับปรุงความเข้ากันได้และขนาดของรูปภาพเนทีฟ ( อ่านเพิ่มเติม)
<plugins>
<!-- ... -->
<plugin>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-aot-maven-plugin</artifactId>
<version>0.11.2</version>
<executions>
<execution>
<id>generate</id>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
ตอนนี้เราจะอัปเดต spring-boot-maven-plugin เพื่อเปิดใช้การรองรับอิมเมจเนทีฟ และใช้ Paketo Builder เพื่อสร้างอิมเมจเนทีฟ
<plugins>
<!-- ... -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<builder>paketobuildpacks/builder:tiny</builder>
<env>
<BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
</env>
</image>
</configuration>
</plugin>
</plugins>
โปรดทราบว่ารูปภาพTiny Builder เป็นเพียงตัวเลือกหนึ่งในหลายตัวเลือก ซึ่งเป็นตัวเลือกที่ดีสำหรับ Use Case ของเราเนื่องจากมีไลบรารีและยูทิลิตีเพิ่มเติมเพียงไม่กี่รายการ ซึ่งช่วยจํากัดพื้นที่ในการโจมตีได้
เช่น หากคุณกำลังสร้างแอปที่ต้องเข้าถึงไลบรารี C ทั่วไปบางรายการ หรือยังไม่แน่ใจเกี่ยวกับข้อกำหนดของแอป full-builder อาจเหมาะกว่า
5. สร้างและเรียกใช้แอปเนทีฟ
เมื่อทุกอย่างพร้อมแล้ว เราควรจะสร้างรูปภาพและเรียกใช้แอปที่คอมไพล์แบบเนทีฟได้
สิ่งที่ควรทราบก่อนเรียกใช้บิลด์มีดังนี้
- ซึ่งจะใช้เวลานานกว่าการสร้างปกติ (2-3 นาที)
- กระบวนการบิลด์นี้อาจใช้หน่วยความจํามาก (2-3 กิกะไบต์)
- กระบวนการสร้างนี้กำหนดให้เข้าถึง Daemon ของ Docker ได้
- แม้ว่าในตัวอย่างนี้เราจะทำตามขั้นตอนด้วยตนเอง แต่คุณยังกำหนดค่าระยะการสร้างเพื่อเรียกใช้โปรไฟล์การสร้างเนทีฟโดยอัตโนมัติได้ด้วย
วิธีสร้างรูปภาพ
mvn spring-boot:build-image
เมื่อสร้างแล้ว เราก็พร้อมดูแอปเนทีฟที่ทำงานจริง
วิธีเรียกใช้แอป
docker run --rm -p 8080:8080 demo:0.0.1-SNAPSHOT
ตอนนี้เราอยู่ในจุดที่เหมาะสําหรับดูทั้ง 2 ด้านของสมการแอปพลิเคชันเนทีฟ
เราเสียเวลาและหน่วยความจําไปบ้างในช่วงคอมไพล์ แต่สิ่งที่ได้คือแอปพลิเคชันที่เริ่มต้นได้เร็วขึ้นมากและใช้หน่วยความจําน้อยลงอย่างมาก (ขึ้นอยู่กับปริมาณงาน)
หากเรียกใช้ docker images demo
เพื่อเปรียบเทียบขนาดของรูปภาพเนทีฟกับต้นฉบับ เราจะเห็นว่าขนาดลดลงอย่างมาก
นอกจากนี้ โปรดทราบว่าใน Use Case ที่ซับซ้อนมากขึ้น ต้องมีการแก้ไขเพิ่มเติมเพื่อแจ้งให้คอมไพเลอร์ AOT ทราบว่าแอปของคุณจะทําอะไรบ้างเมื่อรันไทม์ ด้วยเหตุนี้ ปริมาณงานที่คาดการณ์ได้บางอย่าง (เช่น งานแบทช์) จึงอาจเหมาะกับวิธีนี้มาก ขณะที่งานอื่นๆ อาจให้ประสิทธิภาพที่ดีขึ้น
6. การนำแอปเวอร์ชันเนทีฟของเราไปใช้งาน
หากต้องการทำให้แอปใช้งานได้ใน Cloud Run เราจะต้องส่งอิมเมจเนทีฟไปยังเครื่องมือจัดการแพ็กเกจ เช่น Artifact Registry
1. เตรียมที่เก็บ Docker
เราเริ่มกระบวนการนี้ได้โดยการสร้างที่เก็บข้อมูล ดังนี้
gcloud artifacts repositories create native-image-docker-repo --repository-format=docker \
--location=us-central1 --description="Repository for our native images"
ถัดไป เราต้องการตรวจสอบว่าเราตรวจสอบสิทธิ์เพื่อส่งข้อมูลไปยังรีจิสทรีใหม่แล้ว
gcloud CLI ช่วยให้คุณดำเนินการดังกล่าวได้ง่ายขึ้นมาก
gcloud auth configure-docker us-central1-docker.pkg.dev
2. การพุชรูปภาพไปยัง Artifact Registry
ต่อไปเราจะติดแท็กรูปภาพ โดยทำดังนี้
export PROJECT_ID=$(gcloud config list --format 'value(core.project)')
docker tag demo:0.0.1-SNAPSHOT \
us-central1-docker.pkg.dev/$PROJECT_ID/native-image-docker-repo/quickstart-image:tag2
จากนั้นเราจะใช้ docker push
เพื่อส่งไปยัง Artifact Registry ได้
docker push us-central1-docker.pkg.dev/$PROJECT_ID/native-image-docker-repo/quickstart-image:tag2
3. การทำให้ใช้งานได้กับ Cloud Run
ตอนนี้เราพร้อมที่จะทำให้อิมเมจที่เราจัดเก็บไว้ใน Artifact Registry ใช้งานได้กับ Cloud Run แล้ว โดยทำดังนี้
gcloud run deploy --image us-central1-docker.pkg.dev/$PROJECT_ID/native-image-docker-repo/quickstart-image:tag2
เนื่องจากเราได้สร้างและติดตั้งใช้งานแอปเป็นอิมเมจเนทีฟ เราจึงมั่นใจได้ว่าแอปพลิเคชันของเราใช้โครงสร้างพื้นฐานอย่างคุ้มค่าขณะทำงาน
คุณสามารถเปรียบเทียบเวลาเริ่มต้นของแอปพื้นฐานกับแอปใหม่แบบเนทีฟนี้ด้วยตัวคุณเอง
7. สรุป/ล้างข้อมูล
ยินดีด้วยที่สร้างและทำให้แอปพลิเคชัน Spring Native ใช้งานได้ใน Google Cloud
เราหวังว่าบทแนะนำนี้จะทําให้คุณคุ้นเคยกับโปรเจ็กต์ Spring Native มากขึ้นและอย่าลืมใช้โปรเจ็กต์นี้หากตรงกับความต้องการของคุณในอนาคต
ไม่บังคับ: ล้างข้อมูลและ/หรือปิดใช้บริการ
ไม่ว่าคุณจะสร้างโปรเจ็กต์ Google Cloud สําหรับโค้ดแล็บนี้หรือใช้โปรเจ็กต์ที่มีอยู่ โปรดระมัดระวังการเรียกเก็บเงินที่ไม่จําเป็นจากทรัพยากรที่เราใช้
คุณสามารถลบหรือปิดใช้บริการ Cloud Run ที่เราสร้างขึ้น ลบรูปภาพที่เราโฮสต์ หรือปิดทั้งโปรเจ็กต์
8. แหล่งข้อมูลเพิ่มเติม
แม้ว่าขณะนี้โปรเจ็กต์ Spring Native จะเป็นโปรเจ็กต์ใหม่และโปรเจ็กต์ทดลอง แต่ก็มีแหล่งข้อมูลที่เป็นประโยชน์มากมายที่จะช่วยให้ผู้ใช้งานกลุ่มแรกแก้ปัญหาและมีส่วนร่วมได้
แหล่งข้อมูลเพิ่มเติม
ด้านล่างนี้คือแหล่งข้อมูลออนไลน์ที่อาจเกี่ยวข้องกับบทแนะนำนี้
- ดูข้อมูลเพิ่มเติมเกี่ยวกับคำแนะนำแบบเนทีฟ
- ดูข้อมูลเพิ่มเติมเกี่ยวกับ GraalVM
- วิธีเข้าร่วม
- เกิดข้อผิดพลาดเนื่องจากหน่วยความจำไม่เพียงพอเมื่อสร้างรูปภาพเนทีฟ
- ข้อผิดพลาดที่แอปพลิเคชันเริ่มไม่สำเร็จ
ใบอนุญาต
ผลงานนี้ได้รับอนุญาตภายใต้สัญญาอนุญาตครีเอทีฟคอมมอนส์สำหรับยอมรับสิทธิของผู้สร้าง (Creative Commons Attribution License) 2.0 ทั่วไป