มาลองสร้าง Spring Boot App ด้วย GraalVM Native Image

Phayao Boonon
4 min readFeb 19, 2021

--

Photo by Garett Mizunaka on Unsplash

ปัญหาหนึ่งของ Java Application อย่าง Spring Boot Web Service ที่ต้องทำงานบน JVM นั้นคือใช้ทรัพยากรของระบบอย่างมาก และเป็นจุดด้อยที่สำคัญมากสำหรับระบบที่ทำงานเป็นแบบ Microservice บน Cloud เพราะทำให้มีค่าใช้จ่ายมาก เมื่อเทียบกับภาษาใหม่อย่าง Golang ที่ compile เป็น machine code ที่ทำงานได้เลยโดยไม่ต้องใช้ Virtual Machine เพื่อให้ Application ทำงานได้ แต่ในโลกของ Java ก็มีเครื่องมือหนึ่งที่สร้างมาเพื่อแก้ปัญหานนี้ นั้นคือ GraalVM โดย Oracle ที่จะทำให้ Java Application ทำงานบน GraalVM และแปลงเป็น machine code และทำงานได้โดยไม่ต้องใช้ JVM ทำให้ใช้ทรัพยากรของระบบลดลงมาก (GraalVM เคลมว่าใช้ memory ลดลง 5 เท่า) ดังนั้น ในบทความนี้จะมาสร้าง Spring Boot Web Service ให้ทำงานบน GraalVM ซึ่งแปลง application ให้เป็น machine code โดยที่ Spring มี Spring Native สร้างมาเพื่อรองรับการทำงานบน GraalVM ด้วย

GraalVM

GraalVM เป็น high-performance runtime ที่มีการปรับปรุงความสามารถและประสิทธิภาพของ application อย่างมีนัยสำคัญ ซึ่งเป็นแนวคิดสำหรับ Microservice โดยที่ออกแบบมาสำหรับ application ที่เขียนด้วย Java, JavaScript และ ภาษาที่เป็น LLVM-based อย่าง C และ C++ รวมทั้ง dynamic language อื่นๆ อีกด้วย

ซึ่ง GraalVM ทะลายความแตกต่างของภาษาโปรแกรมและทำให้หลายๆ ภาษาสามารถทำงานได้บน GraalVM อย่างมีประสิทธิภาพ

ติดตั้ง GraalVM

สำหรับการใช้งานบนเครื่องเรานั้นจะต้องติดตั้ง GraalVM ก่อน ซึ่งก็เหมือนการติตั้ง JDK โดยที่ GraalVM จะมาพร้อมกับเครื่องมือที่ใช้สำหรับการ compile application เป็น native image

ซึ่ง GraalVM จะ distribute เป็น 2 edition คือ Community ที่เป็นตัวฟรี และ Enterprise ที่เป็นตัวเสียเงิน (แน่นอนในบทความนี้จะใช้ Community edition)

ตัว Community edition ก็สามารถ download ได้จาก Github ส่วน Enterprise edition จะ download จากเว็บของ Oracle เอง

หลังจากที่ download file มาแล้วก็แตกไฟล์ด้วยคำสั่ง

tar -xvf <graalvm-archive>.tar.gz

และย้าย folder ที่แตกไฟล์แล้วไปไว้ใน Java Virtual Machine ในเครื่องด้วยคำสั่ง

sudo mv <graalvm> /Library/Java/JavaVirtualMachines

สามารถตรวจสอบว่าย้ายไปเรียบร้อยไหม ด้วยคำสั่ง /usr/libexec/java_home -V

ถ้าในเครื่องเรามีหลาย JDK จะต้องตั้งค่า PATH และ เปลี่ยน JAVA_HOME ให้เป็นของ GraalVM เสียก่อน

ตรวจสอบ Java ของเครื่องว่าใช้ GraalVM หรือยัง ด้วยคำสั่ง java -version และใช้ คำสั่ง gu list เพื่อตรวจสอบ GraalVM ในเครื่อง

แต่ถ้าใช้ macOS Catalina ขึ้นไปจะต้องลบ quarantine attribute ด้วยคำสั่งนี้

sudo xattr -r -d com.apple.quarantine path/to/graalvm/folder/

สร้าง Spring Boot Web Service

ในบทความนี้จะสร้าง Spring Boot application ง่ายๆ โดยใช้ Spring WebFlux ด้วย Spring Initializr

และสร้าง HelloController class ง่ายๆ (ในบทความนี้จะสร้างเป็น inner class เลย เพื่อให้ controller กระชับ อยู่ในไฟล์เดียว)

หลังจากนั้นลอง run Spring Boot Web Service ดูว่าทำงานได้หรือไม่ เป็นอันเสร็จสิ้น

ซึ่งในบทความนี้ใช้ maven เป็น build tool ดังนั้นเมื่อ run คำสั่ง mvn package แล้วจะได้ jar ไฟล์ใน folder /target โดยที่เราสามารถ run jar ไฟล์นี้ได้ ด้วยคำสั่ง

java -jar /target/demo-graalvm-0.0.1-SNAPSHOT.jar

เพื่อ run Spring Boot app ขึ้นมาแล้วลองใช้ Postman ยิงไปที่ /hello endpoint ดูว่าได้ response เป็น Hello, World กลับมาหรือไม่

เตรียม build GraalVM Native Image

หลังจากที่ได้สร้าง web service ง่ายๆ แล้วต่อไปก็มาเตรียม Spring Boot project ให้สามารถ build เป็น GraalVM native image ได้ ซึ่ง Spring framework ได้มี Spring Native project (ซึ่งตอนนี้อยู่ใน Spring experiment) มารองรับการสร้าง GraalVM Native Image ดังนั้นเราจะต้องเพิ่ม dependency spring-graalvm-native ใน pom.xml แต่เนื่องจากว่า Spring Native ยังไม่ release เป็น Official และยังอยู่ใน Spring milestone ดังนั้น เราจะต้องเพิ่ม spring-milestones ใน repository และ pluginRepository เพื่อให้ maven resolve dependency ได้จาก spring-milestone repository

ก่อนหน้า Spring Native เวอร์ชั่น 0.8.3 จำเป็นต้องเพิ่ม proxyBeanMethods = false ใน @SpringBootApplication และ @Configuration เพื่อปิดการใช้งาน GCLIB proxies ที่ GraalVM ไม่รองรับ โดยใช้ JDK proxies แทน ดังนั้นใน Spring Boot web service ก็ไม่ต้องแก้ไขอะไร

เนื่องจาก Spring Boot ที่ทำงานบน GraalVM ไม่ได้ทำงานบน JDK โดยตรง ดังนั้นเราจะต้องเพิ่ม main.class ในส่วนของ properties เพื่อให้ Spring Native ใช้ตอน build GraalVM native image

ต่อมาก็ต้องมาเตรียม build script สำหรับ run maven build และ compile native image ซึ่งก็ใช้ compile.sh จาก project ตัวอย่าง ของ Spring Native มาใช้งาน ซึ่งมีส่วนต่างๆ ดังนี้

ส่วนแรกจะเป็นส่วนของ run maven build โดยเริ่มจากลบ folder target และสร้าง folder target/native-image ขึ้นมาใหม่ และใช้คำสั่ง mvn package เพื่อ compile java code และสร้างเป็นไฟล์ .jar ของ Spring Boot app จากนั้นก็แตกไฟล์ .jar ออกมาไว้ใน folder target/native-image

ส่วนต่อไปเป็นการ compile เป็น native image ด้วย GraalVM native image สร้างเป็น machine code

Build GraalVM Native Image

เมื่อเตรียมทุกอย่างเรียบร้อยก็มาลอง build GraalVM native image ด้วย build script compile.sh ซึ่งในส่วนของ compile native image จะใช้เวลานานพอสมควร ขึ้นอยู่กับเครื่องที่ใช้ compile (เครื่องผมใช้เวลานานๆๆๆ มากๆๆๆ)

./compile.sh

หลังจาก compile เรียบร้อยแล้วจะได้ executable file ชื่อเดียวกับ project ใน folder target และสามารถ run ไฟล์นี้ได้เลย ด้วยคำสั่ง ./target/demo-graalvm จะเห็นได้ว่า Spring Boot app ใช้เวลา start time แค่ 0.12 วินาที เท่านั้น

เมื่อเทียบกับ Spring Boot app ที่ run บน JVM ใช้เวลา start time 3.4 วินาที

และในมุมของหน่วยความจำ บน GraalVM ก็ใช้ memory เพียง 24 MB เท่านั้น

เมื่อเทียบกับ บน JVM ที่ใช้ memory ไป 440M

สรุป

จกาที่ได้ลองใช้ GraalVM เบื้องต้นนี้จะเห็นได้ว่า tool นี้สามารถแก้ปัญหาของ Java Application ที่ run บน JVM แล้วใช้ทรัพยากรของเครื่องอย่างมาก ทำให้ Web Service ที่ใช้ Spring Boot มีขนาดเล็กลงอย่างมากและเมื่อทำเป็น Microservice ที่ทำงานบน Cloud ก็ใช้ทรัพยากรและที่แน่นอนใช้เงินน้อยลง ทำให้ application ที่พัฒนาด้วย Java เทียบเคียงได้กับ Golang แม้ว่าของ Golang จะเล็กกว่าแต่ก็ลดช่องว่างลงได้เยอะมาก แต่ GraalVM กับ Spring Boot ก็ยังต้องพัฒนาอีกมากตอนนี้ก็อยู่ใน experiment project อยู่ และคง official release ในอีกไม่นาน

อ้างอิง

--

--

Phayao Boonon

Software Engineer 👨🏻‍💻 Stay Hungry Stay Foolish