Reader small image

You're reading from  Microservices with Spring Boot 3 and Spring Cloud, Third Edition - Third Edition

Product typeBook
Published inAug 2023
Reading LevelIntermediate
PublisherPackt
ISBN-139781805128694
Edition3rd Edition
Languages
Right arrow
Author (1)
Magnus Larsson
Magnus Larsson
author image
Magnus Larsson

Magnus Larsson, an IT industry veteran since 1986, has consulted for major Swedish firms like Volvo, Ericsson, and AstraZeneca. Despite past struggles with distributed systems, today's open-source tools like Spring Cloud, Kubernetes, and Istio offer effective solutions. For the past eight years, Magnus has been helping customers use these tools and shared insights through presentations and blog posts.
Read more about Magnus Larsson

Right arrow

Native-Complied Java Microservices

In this chapter, we will learn how to compile the Java source code in our microservices into binary executable files, known as Native Images. A Native Image starts up significantly faster compared to using a Java VM and is also expected to consume less memory. We will be introduced to the Spring AOT engine introduced in Spring Framework 6 and the GraalVM project and its Native Image compiler, learning how to use them.

We will cover the following topics:

  • When to natively compile Java source code
  • Introducing the GraalVM project and Spring’s AOT engine
  • Handling problems with native compilation
  • Testing and compiling Native Images
  • Testing with Docker Compose
  • Testing with Kubernetes

Even though Spring Framework 6 and Spring Boot 3 come with General Availability (GA) support for building native executables of Spring Boot applications, it must be considered as being in an early stage. At...

Technical requirements

For instructions on how to install the tools used in this book and how to access the source code for this book, see:

  • Chapter 21, Installation Instructions for macOS
  • Chapter 22, Installation Instructions for Microsoft Windows with WSL 2 and Ubuntu

The code examples in this chapter all come from the source code in $BOOK_HOME/Chapter23.

If you want to view the changes applied to the source code in this chapter so you can natively compile the microservices, you can compare it with the source code for Chapter 20, Monitoring Microservices. You can use your favorite diff tool and compare the two folders $BOOK_HOME/Chapter20 and $BOOK_HOME/Chapter23.

When to native-compile Java source code

Java has always been known for its build-once-run-anywhere capability, providing excellent cross-platform support. The Java source code is compiled once into bytecode. At runtime, a Java VM transforms the bytecode into executable code for the target platform using a Just in Time compiler, also known as JIT compilation. This takes some time, slowing down the startup of Java programs. Before the era of microservices, Java components typically ran on an application server, like a Java EE server. After being deployed, the Java component ran for a long time, making the longer startup time less of a problem.

With the introduction of microservices, this perspective changed. With microservices, there comes the expectation of being able to upgrade them more frequently and quickly scale instances for a microservice up and down based on its usage. Another expectation is to be able to scale to zero, meaning that when a microservice is not used, it should...

Introducing the GraalVM project

Oracle has worked for several years on a high-performance Java VM and associated tools, known together as the GraalVM project (https://www.graalvm.org). It was launched back in April 2018 (https://medium.com/graalvm/graalvm-in-2018-b5fa7ff3b917), but work can be traced back to, for example, a research paper from Oracle Labs in 2013 on the subject: Maxine: An approachable virtual machine for, and in, java; see https://dl.acm.org/doi/10.1145/2400682.2400689.

Fun Fact: The Maxine VM is known as a metacircular Java VM implementation, meaning that it is, itself, written in Java.

GraalVM’s VM is polyglot, supporting not only traditional Java VM languages such as Java, Kotlin, and Scala but also languages such as JavaScript, C, C++, Ruby, Python, and even programs compiled into a WebAssembly. The part of GraalVM that we will focus on is its Native Image compiler, which can be used to compile Java bytecode into a Native Image containing...

Introducing the Spring’s AOT engine

The Spring team has also worked on supporting the native compilation of Spring applications for some time. In March 2021, after 18 months of work, the experimental Spring Native project launched a beta release; see https://spring.io/blog/2021/03/11/announcing-spring-native-beta. Based on the experiences from the Spring Native Project, official support for building Native Images was added in Spring Framework 6 and Spring Boot 3; see https://spring.io/blog/2023/02/23/from-spring-native-to-spring-boot-3. To perform the actual native compilation, Spring uses GraalVM’s Native Image compiler under the hood.

The most important feature is Spring’s new AOT engine, which analyzes the Spring Boot application at build time and generates initialization source code and reachability metadata required by the GraalVM Native Image compiler. The generated initialization source code, also known as AOT-generated code, replaces the reflection...

Handling problems with native compilation

Natively compiling Spring Boot applications is, as already mentioned, not yet mainstream. Therefore, you will probably run into problems when trying it out on your own applications. This section will go through a few projects and tools that can be used to handle these problems. Examples of how to use these tools will be provided in the sections below.

The following project and tools can be used to handle problems with native compilation of Spring Boot applications:

  • Spring AOT smoke tests:

    This project contains a suite of tests verifying that the various Spring projects work when natively compiled. Whenever you encounter issues with natively compiling a Spring feature, you should start looking into this project for a working solution. Also, if you want to report a problem with natively compiling a Spring project, you can use tests from this project as a boilerplate to demonstrate the problem in a reproducible way. The project...

Changes in the source code

Before compiling the Java source code in the microservices into native executable images, the source code needs to be updated a bit. To be able to natively compile the microservices, the following changes have been applied to the source code:

  • The Gradle build files, build.gradle, have been updated by adding the GraalVM plugin, adjusting some dependencies, and configuring the bootBuildImage command.
  • Required reachability metadata and custom hints have been added.
  • Build time property files have been added to ensure that required Spring beans are reachable during the AOT processing at build time.
  • Some properties used at runtime have been added to the config-repo to make the natively compiled microservices operate successfully.
  • The configuration to be able to run the GraalVM Native Image tracing agent has been added.
  • The verification script, test-em-all.bash, has been updated since the Docker images no longer include...

Testing and compiling Native Images

Now, it is time to try out the tools for testing and building Native Images!

The following tools will be covered in this section:

  • Running the tracing agent
  • Executing native tests
  • Creating a Native Image for the current OS
  • Creating a Native Image as a Docker image

Since the first three tools require that the GraalVM and its Native Image compiler are installed locally, we must install them first. Next, we will go through the tools one by one.

Installing GraalVM and its Native Image compiler

If you want to try out native tests, the tracing agent, or the native compiler without using Docker, you must first install the GraalVM JDK together with the Native Image compiler.

It can be done by the following steps:

  1. To install GraalVM, SDKman (https://sdkman.io) will be used. If not already installed, it can be installed with the following commands:
    curl -s "https://get.sdkman...

Testing with Docker Compose

We are ready to try out the natively compiled microservices. To use the Docker images that contain the natively compiled microservices, two new Docker Compose files have been created, docker-compose-native.yml and docker-compose-partitions-native.yml. They are copies of docker-compose.yml, and docker-compose-partitions.yml, where the build option has been removed from the definitions of the microservices. Also, the names of the Docker images to use have been changed, so the ones we created in the previous section are used, with names that start with native-.

In this chapter, we will only use docker-compose-native.yml.; feel free to try out docker-compose-partitions-native.yml on your own.

We’ll first get a benchmark using the Java VM-based microservices to compare the startup times and initial memory consumption. We will run the following tests:

  • Use the Java VM-based microservices with AOT mode disabled.
  • Use the...

Testing with Kubernetes

To be able to deploy the natively compiled microservices in Kubernetes, a new environment Helm chart has been added, which has been configured to use the Docker images that contain the natively compiled microservices. The Helm charts can be found in the following folders:

kubernetes/helm/
└── environments
    └── dev-env-native

Another thing we need to consider before deploying the natively compiled microservices to Kubernetes is how to provision the Docker images. We don’t want to run the lengthy native compilation commands again to get new Docker images created in the minikube instance. If we used a Docker registry in this book, we could have pushed the images to the registry, but we haven’t. Instead, we will extract the Docker images from Docker Desktop and import them into the minikube instance, as a workaround for not using a Docker registry.

Move the Docker images from Docker Desktop...

Summary

In this chapter, we were introduced to the new Spring AOT engine and underlying GraalVM project, along with its Native Image compiler. After declaring GraalVM’s plugin in the build file and providing the Native Image compiler with some reachability metadata and custom hints, it can be used to create Native Images. Spring Boot’s Gradle task buildBootImage packages these standalone executable files into ready-to-use Docker images.

The main benefit of compiling Java-based source code into Native Images is significantly faster startup time and less memory usage. In a test where we started up the microservice instances at the same time, we observed 0.2-0.5 seconds startup times for the natively compiled microservices, compared with the 5.5 to 7 seconds required for the Java VM-based microservices for the same test. Also, the natively compiled microservices required less than half of the memory compared to the Java VM-based microservices after running through the...

Questions

  1. How are the Spring’s AOT engine and the GraalVM projects related to each other?
  2. How is the tracing agent used?
  3. What is the difference between JIT and AOT compilation?
  4. What is AOT mode, and how can it be beneficial to use?
  5. What is a native custom hint?
  6. What is a native test, and why is it useful?
  7. How are initial memory usage and startup times affected by natively compiling Java code?

Join our community on Discord

Join our community’s Discord space for discussion with the author and other readers:

https://packt.link/SpringBoot3e

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Microservices with Spring Boot 3 and Spring Cloud, Third Edition - Third Edition
Published in: Aug 2023Publisher: PacktISBN-13: 9781805128694
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €14.99/month. Cancel anytime

Author (1)

author image
Magnus Larsson

Magnus Larsson, an IT industry veteran since 1986, has consulted for major Swedish firms like Volvo, Ericsson, and AstraZeneca. Despite past struggles with distributed systems, today's open-source tools like Spring Cloud, Kubernetes, and Istio offer effective solutions. For the past eight years, Magnus has been helping customers use these tools and shared insights through presentations and blog posts.
Read more about Magnus Larsson