Serverless Architectures With Java

In this article, we’ll explore the core principles of serverless java architecture, explore its key benefits, and unveil how Micronaut’s innovative approach empowers developers to build robust, high-performing serverless applications using Java.

We’ll then show the deployment process, showing you how to seamlessly deploy a Micronaut application as an HTTP Cloud Function on Google Cloud Platform (GCP).

Serverless Architecture

Cloud Computing Evolution

The landscape of computing has undergone a dramatic transformation in recent decades. In the early days of computing, organizations relied on on-premises data centers.

A paradigm shift emerged with the introduction of Infrastructure as a Service (IaaS). The evolution continued with Platform as a Service (PaaS). PaaS offered a platform pre-configured with tools and services for developing and deploying applications.

Software as a Service (SaaS) further democratized access to technology. SaaS applications are readily available over the internet, eliminating the need for local installations or complex configurations.

The latest chapter in this evolving story is serverless computing, with Function as a Service (FaaS) at its core. Here, developers focus solely on writing code for specific functionalities (functions) without worrying about servers, scaling, or infrastructure management.

Parallel to Infrastruture evolution, there was architecture evolution.

Architecture Evolution

Traditional applications often began as monoliths – single, self-contained units containing all functionalities within a single codebase. However, as applications grew in complexity, these single points of failure became cumbersome to maintain and scale.

The limitations of monoliths led to the rise of microservices architecture. This approach breaks down an application into smaller, independent services, each with a well-defined purpose.

The latest stage in this evolution is serverless architecture.

Here, the focus shifts away from managing servers entirely. Developers write code for specific functionalities (functions) that are triggered by events. The cloud provider handles server provisioning, scaling, and management.

You can read my article about emerging software archictectures here

Serverless Java

Serverless architecture is designed to be stateless, meaning individual functions invoked within this architecture don’t retain any information about previous executions.

This means the application needs fresh start for each request. Fresh start, also known as cold start, is one of the main disadvantages of serverless architecture with Java

This was a problem for Java applications relying heavily on reflection, runtime proxy generation, and dynamic class loading.

When a Java application starts, it might use reflection to discover and analyze classes and their relationships. This process can be time-consuming, especially for complex applications with many classes.

Generating proxy classes for numerous interfaces and methods adds overhead during application startup. The time it takes to define the logic and generate the bytecode for these proxies can slow down initial application launch.

While dynamic classloading offers flexibility, searching for and loading classes on demand can add some overhead during application startup compared to having everything loaded upfront.

It become clear that traditional approach to java development is not god fit for serverless architectures.

However, with Java reigning as one of the most popular programming languages on the planet, many companies still depend on it to function and several Frameworks were developed to address this limitations.

Frameworks for Severless Java Architecture

Spring Cloud Function

Spring Cloud Function is a project from the Spring framework that simplifies building and deploying serverless applications in Java.

It provides a layer of abstraction over various cloud provider’s serverless platforms, allowing developers to focus on writing code for their functions and not worry about the underlying infrastructure.

It can be used with GraalVM native images. GraalVM compiles your Java code into a native executable, significantly reducing startup time compared to traditional JAR deployments. This is a powerful technique for minimizing cold starts.

Micronaut Framework

Micronaut takes a unique approach to serverless development. By doing more work upfront during compilation, the framework ensures the application is optimized for fast execution. Additionally, it avoids techniques like reflection and dynamic class loading that can slow things down.

Micronaut’s approach is ideal for GraalVM’s Native Image tool. Because Micronaut avoids techniques that require runtime analysis, GraalVM’s analysis can be more complete, leading to a more optimized native image without any additional configuration needed.

Apache OpenWhisk

Apache OpenWhisk is an open-source, serverless cloud platform for building and deploying applications.

It supports multiple programming languages for writing functions, including Java. OpenWhisk functions are packaged as Docker containers, enabling deployment across diverse platforms and cloud environments. 

Techniques like simulating invocations or keeping functions warm with long-running tasks might be implemented outside of OpenWhisk itself.

These strategies can help reduce cold starts but require additional development effort

Serverless Java Architecture Example

In this example going to deploy a Micronaut application as an HTTP Function to Google Cloud Functions.

We created new maven project in Intellij IDEA and included parent micronaut project in pom.xml

<parent>
   <groupId>io.micronaut.platform</groupId>
   <artifactId>micronaut-parent</artifactId>
   <version>4.5.0</version>
</parent>

Next, we included gcp function and micronaut http client dependencies

<dependency>
   <groupId>io.micronaut.gcp</groupId>
   <artifactId>micronaut-gcp-function-http</artifactId>
   <scope>compile</scope>
</dependency>
<dependency>
   <groupId>com.google.cloud.functions</groupId>
   <artifactId>functions-framework-api</artifactId>
   <scope>provided</scope>
</dependency>
<dependency>
   <groupId>io.micronaut</groupId>
   <artifactId>micronaut-http-client</artifactId>
   <scope>test</scope>
</dependency>

Jackson library for json formatting:

<dependency>
   <groupId>io.micronaut</groupId>
   <artifactId>micronaut-jackson-databind</artifactId>
   <scope>compile</scope>
</dependency>

At the end we add google cloud function plugin so we can run maven goals

<plugin>
   <groupId>com.google.cloud.functions</groupId>
   <artifactId>function-maven-plugin</artifactId>
   <configuration>
       <functionTarget>io.micronaut.gcp.function.http.HttpFunction</functionTarget>
   </configuration>
</plugin>

Next we create MyController class:

package org.example;

import io.micronaut.core.annotation.Introspected;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.*;


@Controller("/hello")
public class MyController {


   @Produces(MediaType.TEXT_PLAIN)
   @Get
   public String hello() {
       return "Hello Response";
   }
}

If we run the application locally and execute in local prompt

curl -w "\n" localhost:8080/hello

“Hello response” message is returned.

Now assuming that you have Google Cloud Platform account and installed cloud-cli and set to appropriate project.

To deploy to google cloud console we need to create jar file.

We execute the maven goal:

mvn package

and go to the target directory of the project and run to deploy function named micronautserverless:

gcloud functions deploy micronautserverless \
    --entry-point io.micronaut.gcp.function.http.HttpFunction \
    --runtime java17 \
    --trigger-http

Choose unauthenticated access when prompted and function should be deployed in couple of minutes.

To execute function from local prompt we need to get the trigger url and save it in environement variable

GCF_HTTP_TRIGGER_URL=$(gcloud functions describe micronautguide \
    --format='value(httpsTrigger.url)')

Finally

curl -w "\n" $YOUR_HTTP_TRIGGER_URL/micronautguide

gives:

Hello Response

Conclusion

The emergence of serverless architectures exposed limitations in traditional Java development. This led to the development of new approaches that eliminate the overhead of reflection, runtime proxy generation, and dynamic classloading.

GraalVM can pre-compile your application code into a native executable (called a “native image”) for specific platforms. This native image eliminates the need for a Java Virtual Machine (JVM) at runtime, leading to significantly faster startup times compared to traditional Java deployments.

The Micronaut framework tackles these challenges head-on by streamlining the development and deployment of serverless applications across various cloud providers.

As an example, we showcased how to effortlessly deploy a simple serverless application to Google Cloud Platform (GCP).

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top