java-microservices-sopra
java-microservices-sopra
Modern Java
Development
Whitepaper
A small throwback in history…
Remember the days when Java was • It was and is a mature, secure, stable
created at Sun Microsystems? More and robust ecosystem
specifically, when James Gosling led
• It is a JVM based-language &
a team of researchers in an attempt
deratives (Java, Kotlin, Groovy, and
to create a new language that would
Scala)
allow electronic consumer devices to
communicate with each other. Efforts • It ensures platform independency
on the language began in 1991 and
On the other hand we do encounter
the first release occurred in 1995.
different challenges to the traditional
The difference between the way Java Java ecosystem:
and other programming languages
• Reflection: Reflection is used to get,
were working was at least revolutionary.
examine or modify the behavior of
Code in other languages was first
methods, classes, and interfaces
translated by a compiler into instructions
at runtime. It’s used by many Java
for a specific type of computer, while
frameworks like Spring to load and
the Java compiler instead converted
instantiate Beans in a dynamic way.
code into something called Bytecode,
which is then interpreted by software • Reflective Data Cache: Because
called the Java Runtime Environment Reflection is slow, frameworks like
(JRE) (or the Java virtual machine). Spring uses a cache to store objects
and beans.
For some decades, Java was the
undisputed number one programming • Big package size.
language. We define it as an interpreted,
• Slow Dynamic Classloading.
moderately complex, multi-threaded,
garbage-collected and powerful • ClassPath scanning.
programming platform. Moreover, the
• Runtime Proxies.
JVM is battletested, powerful, mature
and one of the best Process Virtual • Runtime Byte-code generation:
Machines in the industry. At the time, The interpretation process that
Java was perfect for large, monolithic converts Java bytecode to machine
enterprise applications. code happens in the runtime. As
interpretation is slow, Java uses the
Even today, Java is one of the most
JIT compiler, trying to improve the
popular programming languages for
performance.
many reasons:
• Java 8 memory limits in limits in
• It has outstanding IDE’s supporting
Containers.
the development
2
Unfortunately, with the rise of Microservices • Throughout the years, Java introduced
and Serverless, Java had become new updates and solutions to adapt to
disadvantageous as the JVM seems too many industry trends like:
large (high memory footprint) and slow
• Reactive programming;
(slow start-up time).
• Cloud: Spring Cloud that quickly builds
And, as we can observe in modern
some of the common patterns in
container-based development, the
distributed systems (Routing, Service
container comes with limited resources.
Discovery, circuit breakers, and more);
Considering all these points, we are
reaching fresh challenges in terms of new • New features that have been added
Software Architectures: to the language since Java 8 till now.
• Challenges to use Java in Microservices Finally, the Java Community started to offer
or in Serverless scenarios a modern version of Java with GraalVM.
• Famous tools and frameworks: they are GraalVM is JDK distribution that offers AOT
not optimised for low memory usage compilation and polyglot programming.
and Cold Start Moreover, it compiles directly to Native
Code and is perfectly suitable for Cloud
As a result of this, people started to use
Native Development (because of its low
other, small and faster languages, like
memory footprint and fast start-up time).
Node.js, Rust or Golang in Cloud-Native
development. Thanks to the GraalVM innovation, new
frameworks could arise like Quarkus,
The question therefore is: why do we still
Micronaut & Spring Native for cloud
need Java? But, as we all know... when
development. Before diving into these
we can easily move to other languages
frameworks, we’ll start by taking a closer
like GO & Rust that give us a bitter
look to GraalVM.
performance. But actually, we cannot
provide a straight answer because of We do can tell, Java is here to stay and
Java’s worldwide popularity: will not disappear soon at least!
3
A new era of speed arises
The rise of Java made the services highly scalable.
Microframeworks Especially in a cloud environment, we
can easily scale those services.... but
First, we are taking a small sidestep in the end, our resources are still finite!
to look at Microframeworks. In the
past, we used to build large,monolithic So, we had to rethink it: how can or
architectures, but these had their should we use less resources?
pitfalls. Since they combined multiple Therefore we first need to take a closer
functional modules, whenever one look to microframeworks. To start
module failed, everything failed. So, with, the name refers to minimalistic
we began breaking them down into web application frameworks. These
separate modules, ‘microservices’, frameworks are composed:
which minimised their dependencies
and reduced the impact of one of the • w i t h o u t a u t h e n t i c a t i o n a n d
services failing. These microservices authorisation
were meant to work separately from • without database abstraction via
each other, deployed on separate object-relational mapping
servers, running in their own specific
environment. • without input validation an input
sanitation
With the rise of cloud computing,
these services became serverless Some examples of microframeworks
(function-as-a-service), eliminating the are Javalin, Micronaut, Helidon and
complexity of building and maintaining Quarkus. But fewer modules, functions
the infrastructure that normally and dependencies aren’t sufficient ... If
accompanied the development and we take a closer look at the number
launch of new applications. of lines of code related to start-up
time and memory usage, we can also
Accordingly, by virtualising the immediately notice the impact when
environments on which our services using reflection.
ran, we made them sustainable and
flexible, we automated delivery and
4
The result? Well, there goes our speed.
Just line up and wait in the queue,
please.
5
GraalVM
The “GraalVM” project started out as • Improving the performance of
a research project inside Oracle Labs, languages that run on the JVM
attempting to reduce Java memory (and so reducing application start-
and CPU consumption in addition to up times);
improving the performance of the
• Integrating multi-language support
applications. The project was written in
into the Java ecosystem, as well as
Java and its main focus was to improve
providing a set of programming
the JIT compiler in Java and introduce
tools to do so.
Ahead-of-Time (AOT) compilation
To achieve these goals, GraalVM
called Native-image. GraalVM is a
adds an optimising compiler to the
tool designed for developers to write
JDK, which provides performance
and execute Java code. Specifically,
optimisations for individual languages
GraalVM is a Java Virtual Machine
and interoperability for polyglot
(JVM) and Java Development Kit (JDK)
applications. Besides supporting
created by Oracle. It is a powerful
Java code, GraalVM also supports
runtime that improves application
additional programming languages
performance and efficiency.
including Scala, Kotlin, Groovy, Clojure,
Why is it actually called GraalVM? R, Python, JavaScript, Ruby. In essence,
The word “Graal” comes from old GraalVM allows developers to run code
French for “Grail”. The “Graal” Oracle efficiently in multiple languages and
project started out as a research libraries while in a single application.
project inside Oracle Labs, attempting In addition, GraalVM provides a
to make a Java compiler while being framework for creating language-
fast and easy to maintain. The “VM” agnostic tools like debuggers, profilers,
in “GraalVM” comes from the fact that or other instrumentations. Accordingly,
it runs inside the JVM. it will provide a standardised way
We can define the main objectives of to express and execute programme
GraalVM as: code. This will enable cross-language
research, as well as the development
• Writing a compiler that is faster and of tools that once developed can then
easier to maintain; be applied to any language.
• Having a low footprint and a fast Java
start-up for Cloud and Serverless;
6
The components of GraalVM In object-oriented programming,
Among others, we distinguish three polymorphism (from the Greek
main components that make up the meaning “having multiple forms”) is
core of GraalVM: the characteristic of being able to
assign a different meaning or usage
1. Just-in-time compiler: a high
to something in different contexts -
performance optimising just-in-
specifically, to allow an entity such as
time compiler which is written
a variable, a function, or an object
in modular, maintainable, and
to have more than one form.
extendable fashion in Java itself
to replace the old C++ written (C1/ Object allocations are also improved
C2) HotSpot Java Virtual Machine. through optimisations made in memory-
allocation. For example, GraalVM
2. Ahead-of-time compiler: an
will use partial escape analysis and
ahead-of-time compiler to build
scalar replacement for such tasks. In
native executables.
general, GraalVM can achieve better
3. Multiple languages support: the performance with less memory.
ability to implement language
Potential attack surfaces are also
interpreters. This allows GraalVM
minimised when the ahead-of-time
to be expanded to add additional
compiler compiles Java code into a
languages to the Java ecosystem.
native executable. This is because
It also supports tools such as
only the code required to execute
a language-agnostic debugger,
the application is included. To do this,
profiler and heap viewer.
GraalVM will analyse the application
How does GraalVM work? code, its dependencies, dependent JDK
The GraalVM just-in-time compiler is libraries and VM components.
used to accelerate the performance of
When we compile our Java programme
any Java and JVM-based application
(e.g., using the Java command), we’ll end
without the need for code changes.
up with our source code compiled into
GraalVM can also use its ahead-
the binary representation of our code
of-time native image compiler to
– a JVM bytecode (1). This bytecode
translate Java and JVM applications
is simpler and more compact than
into native platform executables. The
our source code, but conventional
Enterprise version compiler includes
processors in our computers cannot
62 compiler optimisation algorithms,
execute it.
also called phases. Of these
algorithms, some include techniques
for vectorising complex programmes,
code specialisation and large-scale
escape analysis. Compiler phases are
optimised by using techniques such as
aggressive and polymorphic inlining.
7
To be able to run a Java programme, The GraalVM compiler also works
the JVM interprets the bytecode (2). as an ahead-of-time (AOT) compiler,
Since interpreters are usually a lot producing native executables. Given
slower than native code executing Java’s dynamic nature, how does that
on a real processor, the JVM can work exactly?
run another compiler which will now
Unlike JIT mode, where compilation and
compile our bytecode into the machine
execution happen at the same time, in
code that can be run by the processor.
AOT mode the compiler performs all
This so-called just-in-time compiler is
compilations during build time, before
much more sophisticated than the
the execution.
javac compiler, and it runs complex
optimisations to generate high-quality The main idea here is to move all the
machine code. “heavy lifting” — expensive computations
— to build time, so it can be done
GraalVM can compile the code Just-
once. That way, at runtime generated
In-Time (JIT) or Ahead of Time (AOT)
executables can be launched quickly
directly to native image (3).
and finished immediately as everything
There is a general myth that AOT is is pre-calculated and compiled.
faster, which is very true in the first
The GraalVM ‘native-image’ utility
few runs, but there is a possibility that
takes Java bytecode as input and
the JIT might outperform the AOTs,
outputs a native executable. To do so,
as JIT is constantly optimising (Graal
the utility performs a static analysis of
VM) based on the feedback it gets
the bytecode under a closed world
from profiling. JIT normally has a larger
assumption. During the analysis, the
footprint than AOT.
utility looks for all the code that your
For Serverless — it makes more sense application actually uses and eliminates
to go towards AOT, while for long everything that is unnecessary.
running container based/VM based
deployments, JIT might make more
sense.
8
These three key concepts help you • H e a p s n a p s h o t t i n g . H e a p
better understand the Native Image snapshotting in Native Image is
generation process: a very interesting concept and
deserves its own article. During the
• Points-to analysis. GraalVM Native
image build process, Java objects
Image determines which Java classes,
allocated by static initialisers, and all
methods, and fields are reachable
the objects that are reachable, are
at runtime, and only those will be
written onto the image heap. This
included in the native executable.
means that your application starts
The points-to analysis starts with
much faster with a pre-populated
all entry points, usually the main
heap.
method of the application. The
analysis iteratively processes all Still, there are some limitations on using
transitively reachable code paths the Native Image as e.g.:
until a fixed point is reached and
• Dynamic Class Loading: Deploying
the analysis ends. This applies not
jars, wars, etc. at runtime impossible.
only to the application code but
also to the libraries and JDK classes • Reflection: Requires registration via
— everything that is needed for native-image CLI/ API.
packaging an application into a
• Dynamic Proxy: No agents: JMX,
self-contained binary.
JRebel, Byteman, profilers, tracers,
• Initialisations at build time. GraalVM etc.
Native Image defaults to class
initialisation at runtime to ensure
correct behavior. But if Native Image
can prove that certain classes are
safe to initialise, it will initialise them at
build time instead. This makes runtime
initialization and checks unnecessary
and improves performance.
9
MicroProfile
MicroProfile is a community-driven The founding vendors of MicroProfile
specification which is designed to offered their own microservices
provide a baseline platform definition. frameworks:
Firstly, to optimise the Enterprise
• Open Liberty (from IBM)
Java for microservices architecture,
and secondly, to deliver application • WildFly Swarm (from Red Hat) which
portability across multiple MicroProfile was renamed to
runtimes.
Thorntail and finally renamed to
Quarkus
• TomEE (Tomitribe)
Quarkus
Quarkus is a MicroProfile implementation In order to understand the immediate
that focuses on efficiently running Java benefit of using Quarkus, let’s first look
applications in containers in general, at how Java applications run with and
and Kubernetes in particular. without Quarkus.
10
The remaining set of activities like loading Let’s do it the Quarkus way
of the configuration file, scanning the The new way or the Quarkus way to
class path to find the annotated classes optimise the application start-up time
and read annotations, reading the XML is that Quarkus performs most of the
descriptors, starting the activities during the build time instead
of runtime.
thread pool and so on and done
during the runtime when the application Loading of the configuration files,
starts. So this means that since most class path scanning, read and set
of the activities are performed during the properties etc. are performed
the runtime instead of compile time, during the build time. This means that
hence the application startup time is the metadata is only processed once
more when you run your applications during the build time.
using traditional java cloud native
So when your application starts, since
frameworks.
all the metadata is already loaded
As with other programming languages, and set during build time, it minimises
a Java programme begins with the need of dynamic scanning and
source code that can be read by loading of classes during the runtime.
a human. In order to execute the Naturally this results in a significant
instructions of the source text on a improvement in the startup times of
computer, corresponding instructions the applications. So this is the way
are generated in the format of the Quarkus works behind the scenes and
specific processor. the reason for its supersonic, subatomic
nature.
With Java, there is another intermediate
step: The source text is first translated In contrast to the native execution
into an intermediate format, the so- of Java applications, Quarkus offers
called bytecode, as is the case with several advantages. Let’s differentiate
the Python language. The bytecode between the two modes supported
is then executed in the “Java virtual by Quarkus:
machine” (JVM). In order to run a Java
1. Optimisation of the bytecode and
programme on a device, a JVM must
execution in the JVM
be installed on it.
2. Running as native code after
The bytecode is traditionally interpreted
compilation
for execution in the JVM. The bytecode
instructions are translated piece by Java code written with Quarkus can be
piece into machine code instructions executed normally in the JVM. However,
and executed. The process of “just-in- there are considerable advantages in
time compilation” (JIT) is more effective. terms of memory consumption and
With that process, the bytecode is start time of a running application. For
also converted into machine code, achieving this, Quarkus a few tricks up
but further optimizations also come its sleeve.
into play.
11
In particular, a number of time- The savings in memory consumption
consuming steps are moved from the and application start-up time achieved
execution to the build process. with AOT compilation, are nothing short
of breathtaking.
This includes the steps that otherwise
occur every time a Java application Pros and Cons of Quarkus?
is executed:
Pros:
• Loading and parsing configurations
• It’s a user friendly (JEE & Spring
• Scanning the Java class path and devs), solid framework:
resolving annotations
• “best of breed” framework standards,
• Creating entity models for databases e.g. Eclipse
or the like whereapplicable
MicroProfile, Spring Dependency
With Quarkus, these steps are Injection, Hibernate ORM
carried out once and the results are
• High performance:
cached for quick retrieval. Further
performance optimisation comes in • fast application start-up time;
the form of Quarkus reducing the
• low memory consumption;
amount of dynamic information
available at runtime. This is replaced • almost immediate scaling of services;
by corresponding static constructs.
• lower space requirements for native
This is particularly useful with regard
images.
to use in containers. A containerised
application is usually not changed Cons:
anyway and always runs in the same
• Reducing the dynamic information
environment.
generated during runtime can lead
The second mode supported by to problems in some scenarios.
Quarkus for running Java applications
• The severely limited possibilities for
is even more interesting. With “ahead-
introspection may make it difficult to
oftime compilation” (AOT), directly
debug an application.
executable machine code is generated
from the Java source text instead of • The highly-optimised build process
bytecode, meaning there is no longer for native images takes a long time…
any need for a JVM on the target
hardware. The programme only runs
on specific processor architecture
and has to be recompiled for other
platforms. However, this restriction is
usually irrelevant for use in containers.
12
Micronaut
Micronaut is a modern Java framework Another feature is its first-class support
that can be used to build microservices for reactive programming, for both
and serverless applications tailored for clients and servers. The choice of a
JDK and GraalVM. It is developed by specific reactive implementation is left
the creators of the Grails framework to the developer as both RxJava and
and sponsored by Object Computing, Project Reactor are supported.
Inc. Micronaut development started
Micronaut also has several features
on early 2018, the 1.0.0 version was
that make it an excellent framework for
released on October 2018.
developing cloud-native applications.
Micronaut is an open source JVM- It supports multiple service discovery
based software framework for building tools such as Eureka and Consul, and
lightweight, modular applications and also works with different distributed
microservices. It is known for its ability tracing systems such as Zipkin and
to help developers create applications Jaeger.
and microservices with small memory
Micronaut provides natively support
footprints and short start-up times
to many cloud features:
(logically, same specs as Quarkus).
• Distributed Configuration with:
It’s created to address some of the
weaknesses of Spring/ Spring Boot. • HashiCorp Consul
Developed by OCI, the same company
• HashiCorp Vault
that created Grails, Micronaut is a
framework designed to make creating • Spring Cloud Config
microservices quick and easy. While
• AWS Parameter Store
Micronaut contains some features that
are similar to existing frameworks like • Service Discovery with:
Spring, it also has some new features
• Consul
that set it apart. And with support for
Java, Groovy, and Kotlin, it offers a • Eureka
variety of ways to create applications.
• Kubernetes
Main Features
• AWS Route 53
One of the most exciting features
of Micronaut is its compile time • Serverless Functions: many features
dependency injection mechanism. Most are provided to make it easier to
frameworks use reflection and proxies write functions, run and deploy
to perform dependency injection at them to Function as a Service
runtime. Micronaut, however, builds its (FaaS) providers such as AWS
dependency injection data at compile Lambda or dedicated containers
time. The result is a faster application such as Open FaaS.
start-up and smaller memory footprints.
13
Micronaut projects can also be the build phase, the memory usage and
generated with an online generator: start-up times are low. Such features are
Micronaut Launch. crucial when working with serverless
functions (cf. microframeworks).
The framework was created from
the ground up to support work with So, when should you use it?
microservices and serverless functions. Let’s say, you need native images but
The creators advertise it as a natively you cannot handle living on the bleeding
cloud-native stack, meaning various edge or having frequent updates or
aspects of cloud deployment (service you may need something special,
discovery, distributed tracing, fast Micronaut might be your solution. It’s
start-up, and small memory footprint) can be used for Spring MVC/Spring
have been considered while designing Boot, which is the most dominant
the framework. Although it is cloud- Server-Side framework in Java. It also
focused, we can create command-line uses the conventional OpenJDK, but
applications as well.` this will slowly lose its charm in Cloud-
Native Java Development.
Because of the ahead-of-time
compilation and resolving DI during
Spring Native
Spring is a very popular framework as The main difference between Spring
it helps to build Java web applications and Spring Native images:
easily and quickly (compared to the
• No class lazy loading as everything
old Java frameworks). It uses the
shipped in the executables will be
conventional OpenJDK and introduces
loaded in memory on start-up;
many features and integrations with
other technologies like Spring Boot, • Classpath scanning is fixed at build
Spring Cloud, Spring Data, Spring AWS time;
and Project Reactor.
• Static analysis of your application
The biggest competition today is about from the main entry point, is
building more efficient Java applications performed at build time;
for the Cloud ecosystem and therefore
• Removing the unused parts of the
logically the Spring community wants
codebase at build time;
to be a part of this competition.
That’s why Spring declared a new • Configuration is required for
solution called Spring Native which reflection, resources, and dynamic
will use GraalVM for Cloud-Native proxies.
development.
14
It’s time for a line-up
Quarkus Spring Native
• It is suitable for a wide range of • Spring is probably the most popular
different application scenarios. Java framework for web applications,
having also a broad long-term
• Other frameworks are more specific
existing open source community.
to some extent.
• Spring Native is based on GraalVM
• It features a large new community.
and, in addition to the creation of
• It has fast bug fixes and feature microservices, it supports reactive
updates.
• programming and live reload. In a
Micronaut performance comparison, Quarkus
• With the Micronaut framework, beats Spring Native.
microservices and serverless
• An existing Spring project can be
applications can be programmed
migrated to Quarkus relatively easily.
in Java.
• On the other hand, Quarkus comes
• As with Quarkus, GraalVM is used
with a more steap learning curve.
here.
Whichever framework you choose, it is
• It is less performant then Quarkus.
evident that Java is ready for a new
• It is supported by OCI (company era of speed and innovation. Now it
which developed Grails) is time for you to jump on board!
15
European tech leader recognised for its consulting, digital services and
software development, helps its clients drive their digital transformation to
obtain tangible and sustainable benefits. It provides end-to-end solutions to
make large companies and organisations more competitive by combining
in-depth knowledge of a wide range of business sectors and innovative
technologies with a fully collaborative approach. Sopra Steria places people
at the heart of everything it does and is committed to putting digital to work
for its clients in order to build a positive future for all. With 50,000 employees
in nearly 30 countries, the Group generated revenue of €5.1 billion in 2022.
soprasteria.be
16