BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles A Guide to the Quarkus 3 Azure Functions Extension: Bootstrap Java Microservices with Ease

A Guide to the Quarkus 3 Azure Functions Extension: Bootstrap Java Microservices with Ease

Bookmarks

Key Takeaways

  • Serverless architecture automatically scales functions up and down in relation to incoming network traffic to the application. It enables enterprise companies to reduce the cost of infrastructure maintenance.
  • Azure Functions provides a serverless platform on Azure cloud. Developers can build, deploy, and manage event-driven serverless functions using multiple languages and Azure services integration.The platform aims to provide a good developer experience.
  • Quarkus enables integrating Azure Functions with various endpoints (e.g., Funqy, RESTEasy Reactive) with the goal of accelerating the inner loop development.
  • Quarkus 3 introduces a new Azure Functions integration that allows developers to bootstrap Quarkus microservices automatically using CDI beans.

A glance at serverless

"Why does serverless matter?" This is a common question by IT professionals in many industry events when I present a serverless topic, regardless of which roles they’re working for. To answer this question, I’ll give you a quick overview of serverless in terms of background, benefits, and technology.

For decades, enterprise companies spent tons of time and effort to reduce the infrastructure maintenance cost and maximize infrastructure resource utilization. They have accomplished much by modernizing applications, virtualization, and containerization.

But this journey can’t end as long as they continue to adopt new technologies and develop new business models on top of the technologies. In the meantime, the enterprises realized that many applications didn’t need to run all the time (e.g., 24 x 7 x 365). They only used those applications for particular business use cases a few times per week or even once a month after they traced the usage metrics.

The serverless architecture was designed to solve this challenge by hibernating applications when there’s no incoming network traffic to the application workloads. Then, the applications will respond quickly when they have new traffic.

Hyperscalers have started to provide serverless technologies for years with their cloud services, such as AWS Lambda, Azure Functions, and Google Functions to catch up with this market demand. Those serverless services allow developers to choose multiple programming languages such as Java, JavaScript, Python, C#, Ruby, Go, and more for the application runtimes.

In this article, we’ll focus on serverless Java with Azure Functions in terms of how Quarkus, a new Kubernetes native Java framework, integrates Java microservices into Azure Functions with an improved developer experience.

Key benefits of using Azure Functions

Let’s take a step back to understand why developers need to deploy serverless applications to Azure Functions among the other serverless platforms. Azure Functions is a serverless computing service that enables developers to build event-driven, scalable applications and services in the Azure cloud without managing the underlying infrastructure. Here are key benefits to knowing about Azure Functions:

  • Supported multiple languages: Azure functions allow developers to choose the programming language, including Java, JavaScript, Python, C#, and PowerShell, that they are most comfortable with to build their serverless applications.
  • Event driven: Azure Functions are triggered by events such as data changes or queue messages. These triggers can be configured using bindings to connect to various services, such as Azure Event Grid, Azure Blob Storage, or Azure Cosmos DB.
  • Cost effective: Azure Functions charges the resources you only use regarding the number of executions, execution time, and memory consumption of the functions. A free tier is also available for developers to experiment with function deployment.
  • Integration with other Azure services: Azure Functions can be integrated with other Azure services such as Azure Logic Apps, Azure Stream Analytics, and Azure API Management. This makes building complex workflows and integrating them with other systems easy.
  • Accelerating outer loop: Azure Functions enables developers to deploy and manage functions easily and quickly using Azure portal, Visual Studio, or command-line tools. This accelerates the outer loop process for developers in terms of easy-to-test and deploying changes to serverless functions quickly.
  • Scalability: Azure Functions automatically scale up or down based on the incoming network traffic. This means developers don't have to worry about managing resources during peak usage periods.
  • Monitoring and logging: Azure Functions provides built-in monitoring and logging capabilities through Azure Monitor. Developers can monitor function execution, track function dependencies, and troubleshoot issues using logs.

Overall, Azure Functions provides a powerful and flexible platform for building serverless applications in the cloud. It offers a wide range of features and integration options that make it easy for developers to build, deploy, and manage their applications.

How Quarkus makes Azure Functions even better

Quarkus is a new Kubernetes native Java framework that enables developers not only to optimize Java applications in Kubernetes by extremely fast startup and response time based on fast-jar and native executables but Quarkus also provides great developer experience by Live Coding, Dev services, Dev UI, Continuous Testing, and Remote Development. For example, if you had a chance to attend developer conferences, you could easily find out how many Java developers were keen to modernize traditional Java frameworks to Quarkus due to these out of box features and benefits.

Since the very early days of the Quarkus community, Quarkus offered Azure Functions extensions for developers to write deployable cloud-native microservices with various endpoints such as RESTEasy Reactive, Undertow, Reactive Routes, and Funqy HTTP on Azure Functions runtime.

This journey didn’t end but arrived in a new era of the Quarkus 3. New Azure Function classes you write with Context and Dependency Injection (CDI) and Arc beans are automatically integrated with Quarkus. This means that when developers can implement Azure Functions using @Inject annotation, Quarkus will be automatically bootstrapped. Developers can also select the function’s lifecycle scope, such as application and request Java beans as normal.

You might ask, "What is the difference between Quarkus Funqy and a new Azure Function? Why would I need to use the new Azure Functions with Quarkus rather than Quarkus Funqy?" Quarkus Funqy can only be invoked by HttpTriggers at this moment. Instead, Azure Functions have different event types that can be invoked, such as HttpTrigger, BlobTrigger, and CosmosDBTrigger and additional supported bindings.

Quarkus Funqy was designed for the lowest common denominator to be a portable, simple, and functional layer across multiple serverless platforms such as Azure Functions, AWS Lambda, Google Cloud Functions, and Kubernetes Knative.

Getting started developing Azure Functions with Quarkus

Let’s give it a try to create a new Quarkus project to implement Azure Functions from scratch. The following step-by-step tutorial helps you easily understand how the Azure Functions and Quarkus integration works and what the Java function code looks like.

1. Create a new function project

Use the Quarkus command (CLI) to generate a new project.

You can also use Maven or Gradle package tools, but the Quarkus CLI provides better developer experiences in creating projects, managing extensions, building and deploying applications with auto-completion, and shortening commands using the underlying project build tool.

Run the following command in your local terminal to create a new Quarkus project by adding a quarkus-azure-function extension:

quarkus create app quarkus-azure-function 
--extension=quarkus-azure-functions

The output should look like this:

-----------
selected extensions: 
- io.quarkus:quarkus-azure-functions

applying codestarts...
  java
  maven
  quarkus
  config-properties
  dockerfiles
  maven-wrapper
  azure-functions-example

-----------
[SUCCESS] ✅  quarkus project has been successfully generated in:
--> /YOUR_WORKING_DIR/quarkus-azure-function
-----------
Navigate into this directory and get started: quarkus dev

Let’s verify that the Azure Functions and CDI injection libraries were installed in your local file system. Open the pom.xml file and look at whether the following dependencies are appended automatically.

    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-azure-functions</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-arc</artifactId>
    </dependency>
    <dependency>
      <groupId>com.microsoft.azure.functions</groupId>
      <artifactId>azure-functions-java-library</artifactId>
    </dependency>

You can also find the azure-functions-maven-plugin in the pom.xml file that you let package the functions and deploy them to Azure Function App.

<plugin>
 <groupId>com.microsoft.azure</groupId>
  <artifactId>azure-functions-maven-plugin</artifactId>
  <version>${azure.functions.maven.plugin.version}</version>
  <executions>
    <execution>
      <id>package-functions</id>
        <goals>
          <goal>package</goal>
        </goals>
    </execution>
  </executions>
<configuration>
  <appName>${functionAppName}</appName>
    <resourceGroup>${functionResourceGroup}</resourceGroup>
    <appServicePlanName>java-functions-app-service-plan</appServicePlanName>
    <region>${functionAppRegion}</region>
    <runtime>
      <os>linux</os>
      <javaVersion>11</javaVersion>
    </runtime>
    <appSettings>
      <property>
        <name>FUNCTIONS_EXTENSION_VERSION</name>
         <value>~4</value>
      </property>
    </appSettings>
  </configuration>
</plugin>

2. Explore the sample function code

Open the Function.java file in the src/main/java/org/acme directory. Look at the GreetingService that injects a CDI bean into Azure Functions when the response body content is built.

    @Inject
    GreetingService service;

As normal Azure Function development using Java, you can still use the @FunctionName annotation to make a method become an Azure Function that the HTTP request will trigger.

    @FunctionName("HttpExample")
    public HttpResponseMessage run(
            @HttpTrigger(
                name = "req",
                methods = {HttpMethod.GET, HttpMethod.POST},
                authLevel = AuthorizationLevel.ANONYMOUS)
                HttpRequestMessage<Optional<String>> request,
            final ExecutionContext context) {
        context.getLogger().info("Java HTTP trigger processed a request.");

        // Parse query parameter
        final String query = request.getQueryParameters().get("name");
        final String name = request.getBody().orElse(query);

        if (name == null) {
            return request.createResponseBuilder(HttpStatus.BAD_REQUEST).body("Please pass a name on the query string or in the request body").build();
        } else {
            return request.createResponseBuilder(HttpStatus.OK).body(service.greeting(name)).build();
        }
    }

3. Test Azure Functions locally

Let’s tweak the function a little bit before you verify it locally. Go back to the Function.java file and change the function to "greeting".

@FunctionName("greeting")

Open the GreetingService.java file and replace the return code with the following line:

return "Welcome to Azure Functions with Quarkus, " + name;

Let’s run your function with a simulated local Azure Functions environment. Run the following maven command:

Note that you need to make sure to use Java 11 with setting JAVA_HOME properly and Azure Functions Core Tools version 4 on your local environment.

./mvnw clean package azure-functions:run

The output should end up with the following logs:

Functions:

        greeting: [GET,POST] http://localhost:7071/api/greeting

For detailed output, run func with --verbose flag.
Worker process started and initialized.
...
INFO: quarkus-azure-function 1.0.0-SNAPSHOT on JVM (powered by Quarkus 3.0.4.) started in 0.353s.

You might have a question. "Can I use the Quarkus dev mode to test the functions while I keep changing the code (aka Live Coding)?" Unfortunately, Quarkus dev mode doesn’t support Azure Functions integration at this time (Quarkus 3.0).

Invoke the REST API to access the function locally. Run the following curl command:

curl -d "Daniel" http://localhost:7071/api/greeting ; echo

The output should look like this:

Welcome to Azure Functions with Quarkus, Daniel

Stop the simulated environment by pressing CTRL-C.

4. Deploy to Azure Cloud

First of all, make sure to log in to Azure Cloud from your local environment using the following az command. If you haven’t installed the az command, find more information here on How to install the Azure CLI.

az login

You will use the azure-functions-maven-plugin again to deploy the greeting function to Azure Function App. Run the following maven command:

./mvnw clean package azure-functions:deploy

The output should end up with BUILD SUCCESS message including a HTTP Trigger URL:

[INFO] HTTP Trigger Urls:
[INFO]   greeting : https://quarkus-azure-function-1681980284966.azurewebsites.net/api/greeting

Let’s go to Azure Portal and navigate to Function App menu, as shown in Figure 1. Then, you will see a deployed function (e.g. quarkus-azure-function-1681980284966).

[Click on the image to view full-size]

Figure 1. Azure Function App

Select the function name to find more information about the function in terms of the resource group, trigger URL, and metrics, as shown in Figure 2.

[Click on the image to view full-size]

Figure 2. Azure Function App Detail

Invoke the REST API to access the function on Azure Function App. Run the following curl command:

curl -d "Daniel on Azure" https://YOUR_TRIGGER_URL/api/greeting ; echo

The output should look like this:

Welcome to Azure Functions with Quarkus, Daniel on Azure

Starting the function based on the cold start strategy will take a few seconds.

Summary

This article showed how Quarkus 3 integrates Azure Functions programmatically using various triggers and bindings that Azure Functions provides. Developers can also understand the differences between Quarkus Funqy and Azure Functions integration for implementing serverless applications.

From the best practice perspective, you might wonder if you need to implement multiple functions (e.g., endpoints) in a single application (e.g., Jar file) or only one function in a single application. There are always tradeoffs, in terms of fault tolerance, maintenance, resource optimization, and startup time. For example, if you only care about fast startup and response time, one function in one application would be better to implement due to the smaller size of a packaged application. But it won’t be easy to maintain many functions after you add more business services along with this development practice.

Additional Resources:

About the Author

Rate this Article

Adoption
Style

BT