Moment For Technology

Implement microservice features using Quarkus and MicroProfile

Posted on Aug. 8, 2022, 6:32 p.m. by Lance Rivas
Category: The back-end Tag: The back-end java

I have written three articles about Quarkus before, and talked about the small and fast of Quarkus.

I have been planning to write a Quarkus ecology-related article, but it has been stalled because I have been busy with Meetup recently. Just saw this article, take it to translate, complete the cloud native "micro services" this piece.

This article is translated as Implementing Microservicilities with Quarkus and MicroProfile.

Why use microservice features?

In microservices architecture, an application is made up of several interconnected services that work together to implement the required business functions.

Therefore, a typical enterprise microservices architecture looks like this:

At first, implementing an application using a microservice architecture seems easy.

However, because of the singleton architecture there are no new challenges, so it is not a container

Examples include fault tolerance, service discovery, extensibility, logging, and tracing.

To address these challenges, every microservice should implement what we at Red Hat call "microservice features."

  • The term refers to a list of cross-domain concerns that services must implement to solve, in addition to business logic, as shown in the following figure:

Business logic can be implemented in any language (Java, Go, JavaScript) or any framework (Spring Boot, Quarkus), but the following concerns should be implemented around business logic:

API: The service is accessed through a defined set of API operations. For example, HTTP is used as a protocol for RESTful Web apis. In addition, a tool such as Swagger can be used to document the API. Service Discovery: A service needs to discover other services.

Invocation of a service: Once the service is found, it needs to be invoked with a set of parameters and the response selectively returned.

Elasticity: One of the key features of a microservice architecture is that each service is elastic, which means it can be scaled independently based on parameters such as the criticality of the system or the current workload. (Translator's Note: The elasticity here is only the elasticity of resources.)

Resiliency: In microservice architectures, we should keep failures in mind when developing, especially when communicating with other services. In a singleton application, the entire application is started or closed. However, when this application is decomposed into a microservice architecture, the application is made up of multiple services, all of which are interconnected over the network, meaning that some parts of the application may be running while others may fail. Failure containment is important to avoid error propagation through other services. Resiliency (or application resiliency) is the ability of an application/service to respond to problems and still provide the best results. (Elasticity is related to fault tolerance, resilience to failure)

Pipelines: Services should be deployed independently without any form of choreography. Therefore, each service should have its own deployment pipeline.

Authentication: One of the key aspects of security in microservices architecture is how calls between internal services are authenticated/authorized. Web tokens (usually tokens) are the preferred way to securely represent declarations in internal services.

Logging: In a singleton application, Logging is simple because all the components of the application run on the same node. Then components are now distributed across multiple nodes as services, so a unified logging system/data collector is required to have a complete logging view.

Monitoring: Measuring system performance, understanding the overall health of your application, and alerting when problems occur are key aspects of keeping microservices-based applications running correctly. Monitoring is a key aspect of controlling your application.

Tracing: Tracing the progress of processes and data used to visualize applications. As developers/operations people, this is especially useful when we need to check the user's journey through the application.

Kubernetes is becoming the de facto tool for deploying microservices. This is an open source system for automating, orchestrating, extending, and managing containers.

With Kubernetes, only three of the ten microservice features are covered.

** Service discovery ** is implemented through the concept of Kubernetes service. It provides a way to group Kubernetes pods (as a whole) using stable virtual IP and DNS names. The discovery service simply requests the service name of Kubernetes as hostname.

Using Kubernetes, services can be invoked easily because the platform itself provides the network needed to invoke any service.

From the beginning, Kubernetes has been thinking about elasticity (or scalability), such as runtime kubectl scale Deployment myService --replicas=5 command, Myservice Deployment scales to five copies or instances. The Kubernetes platform is responsible for finding the right nodes, deploying the service, and always keeping the required number of copies up and running.

But what about the rest of the microservice features? Kubernetes covers only three of them, so how do we implement the rest?

Depending on the language or framework used, there are many strategies you can follow. But in this article, we'll learn how to implement some of these strategies using Quarkus.

What is Quarkus?

Quarkus is a full-stack Kubernetes native Java framework for Java Virtual Machines (JVMS) and native-compiled Java, specifically optimized for containers to make Java an efficient platform for Serverless, cloud and Kubernetes environments.

Instead of reinventing the wheel, Quarkus uses well-known enterprise-grade frameworks backed by standards/specifications and makes them compilable to a Instead of reinventing the wheel, Quarkus uses a well-known enterprise-level framework backed by standards/specifications and compiles it into binaries using GraalVM.

What is MicroProfile?

Quarkus integrates with the MicroProfile specification to migrate an enterprise Java ecosystem into a microservice architecture.

In the figure below, we see all the apis that make up the MicroProfile specification. Some apis (such as CDI, JSON-P, and JAX-RS) are based on the Jakarta EE (formerly Java EE) specification. The rest is developed by the Java community.

Let's Implement API, Invocation, Resilience, authentication, logging, monitoring, and tracing microservicilities using Quarkus. Let's use Quarkus to implement API, invocation, elasticity, authentication, logging, monitoring, and tracing microservices features.

How to use Quarkus to implement microservices features

An introduction to

The quickest way to get started with Quarkus is by selecting the dependencies you want on the start page. For the current example, select the following dependencies to meet microservice requirements:

API: RESTEasy JAX-RS, RESTEasy JSON-b, OpenAPI call: REST Client JSON-B Flexibility: Fault Tolerance Certification: JWT Record: GELF Monitoring: Micrometer Metrics Tracking: OpenTracing

We can manually select the respective dependencies, or browse the following links for Quarkus microservice feature generators, all of which will be selected. Then press the Generate Application button to download the ZIP file that contains the bracket application.


For the current example, a very simple application was generated with just two services. A service called "Rating Service" returns a rating for a given book, while another service called "Book Service" returns information about a book and its rating. All calls between services must be authenticated.

In the figure below, we see an overview of the entire system:

The rating service has been developed and provided as a Linux container. Start the service on port 9090 by running the following command:

Docker run - rm - ti - 9090 p: 8080 quay. IO/lordofthejars/rating - service: 1.0.0Copy the code

To verify the service, please send a request to http://localhost:9090/rate/1

curl localhost:8080/rate/1 -vv

GET/rate / 1 HTTP / 1.1
 Host: localhost:8080
The user-agent: curl / 7.64.1
 Accept: */*
 HTTP / 1.1 401 Unauthorized
 www-authenticate: Bearer {token}
 Content-Length: 0
Copy the code

The returned status code is 401 Unauthorized because no token (JWT) is included with the request to provide authorization information. You must have a valid Group Echoer token to access the rating service.


Quarkus uses the well-known JAX-RS specification to define RESTful Web apis. Behind the scenes, Quarkus uses the RESTEasy implementation to work directly with the vert. X framework without using Servlet technology.

Let's define an API for the book service that implements the most common operations:


public class BookResource {

   public Book book(@PathParam("bookId") Long bookId) {
    // logic

   public Response getBook(Book book) {
       // logic

       return Response.created(

   public Response delete(@PathParam("bookId") Long bookId) {
       // logic

       return Response.noContent().build();

   public Response searchBook(@QueryParam("description") String description) {       
       // logic

       returnResponse.ok(books).build(); }}Copy the code

The first thing to notice is that four different endpoints are defined:

  • GET /book/{bookId}Use the GET HTTP method to return information about a book with its rating. The return element is automatically unmarshalled into JSON.
  • POST /bookInsert a book as body content using the POST HTTP method. The body content is automatically marshalled from JSON to Java objects.
  • DELETE /book/{bookId}DELETE a book by its ID using the DELETE HTTP method.
  • GET /book/search? description={description}Search books by title.

Pay attention to the second thing is the return type, and sometimes is a Java object, sometimes is a Java instance javax.mail. Ws. Rs. Core. The Response. When a Java object is used, it is serialized from the Java object to the media type set in the @Produces annotation. In this particular service, the output is a JSON document. With this Response object, we have fine-grained control over what is returned to the caller. You can set the HTTP status code, the header, or what is returned to the caller. Depending on the usage scenario, there is a preference for one method over another.


With the API defined for accessing the book service, it is time to develop a piece of code that invokes the rating service to retrieve the book's rating.

Quarkus uses the MicroProfile Rest Client specification to access external (HTTP) services. It provides a type-safe way to invoke RESTful services over HTTP through some JAX-RS 2.0 API for consistency and easier reuse.

The first element to be created is an interface that represents the remote service using JAX-RS annotations.



public interface RatingService {
   Rate getRate(@PathParam("bookId") Long bookId);

Copy the code

When the getRate() method is called, a remote HTTP call is invoked at /rate/{bookId} replacing the bookId with the value set in the method parameter. It is important to annotate the interface with the @RegisterRestClient annotation. Then the RatingService interface needs to be injected into BookResource to execute the remote calls. When the getRate() method is called, the remote HTTP request replaces the set of parameter values bookId uses in the method by calling /rate/{bookId}. It is important to annotate interfaces with the @registerRestClient annotation.

RatingService then needs to inject the interface into BookResource to perform the remote call.


RatingService ratingService;

public Book book(@PathParam("bookId") Long bookId) {
    final Rate rate = ratingService.getRate(bookId);

    Book book = findBook(bookId);
    return book;
Copy the code

The @RestClient annotation injects a proxied instance of the interface, providing the implementation of the client. The last thing is to configure the service location (the hostname part). In Quarkus, the configuration properties are set in src/main/resources/ file. To configure the location of the service, we need to use the fully qualified name of the Rest Client interface with URL as key, And the location as a value: The @restClient annotation injects a proxy instance of the interface that provides the client implementation.

The last thing is to configure the service location (hostname section). In Quarkus configuration properties in SRC/main/resources/application Settings in the properties files. To configure the location of the service, we need to use the standard name of the Rest Client interface, with the URL as the key and location as the value:

Copy the code

The mutual authentication issue must be addressed before the assessment service can be properly accessed without the 401 Unauthorized issue.

The authentication

Token-based authentication allows systems to authenticate, authorize, and authenticate identities based on security tokens. Quarkus is integrated with the MicroProfile JWT RBAC security specification to protect services using JWT tokens.

To secure the endpoint with MicroProfile JWT RBAC security, we simply annotate the method with @rolesallowed annotations.

public Book book(@PathParam("bookId") Long bookId)
Copy the code

We then configure the issuer of the token and the location of the public key to verify the token's signature in the file:

mp.jwt.verify.publickey.location= ken/ mp.jwt.verify.issuer= the code

This extension validates automatically: the token is valid; The issuer was right; The token has not been modified; Signature is valid; No expiration date.

The two book services and rating services are now protected by the same JWT publisher and key, so communication between services requires Authentication provided in the valid bearer user Authentication header of the token.

Rating service up and running, let's start the book service with the following command:

./mvnw compile quarkus:dev
Copy the code

Finally, we can make a request to get book information providing a valid JSON Web Token as a bearer token. The generation of the token is out of the scope of this article, and a token has been already generated: Finally, we can request the book information that provides a valid JSON Web token as the bearer.

Token generation is beyond the scope of this article, and tokens have already been generated:

curl -H "Authorization: Bearer eyJraWQiOiJcL3ByaXZhdGVLZXkucGVtIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJqZG9lLXVzaW5nLWp3dC1yYmFjIiwiYXVkIjoid XNpbmctand0LXJiYWMiLCJ1cG4iOiJqZG9lQHF1YXJrdXMuaW8iLCJiaXJ0aGRhdGUiOiIyMDAxLTA3LTEzIiwiYXV0aF90aW1lIjoxNTcwMDk0MTcxLCJpc 3MiOiJodHRwczpcL1wvcXVhcmt1cy5pb1wvdXNpbmctand0LXJiYWMiLCJyb2xlTWFwcGluZ3MiOnsiZ3JvdXAyIjoiR3JvdXAyTWFwcGVkUm9sZSIsImdyb 3VwMSI6Ikdyb3VwMU1hcHBlZFJvbGUifSwiZ3JvdXBzIjpbIkVjaG9lciIsIlRlc3RlciIsIlN1YnNjcmliZXIiLCJncm91cDIiXSwicHJlZmVycmVkX3VzZ XJuYW1lIjoiamRvZSIsImV4cCI6MjIwMDgxNDE3MSwiaWF0IjoxNTcwMDk0MTcxLCJqdGkiOiJhLTEyMyJ9.Hzr41h3_uewy-g2B-sonOiBObtcpkgzqmF4b T3cO58v45AIOiegl7HIx7QgEZHRO4PdUtR34x9W23VJY7NJ545ucpCuKnEV1uRlspJyQevfI-mSRg1bHlMmdDt661-V3KmQES8WX2B2uqirykO5fCeCp3wom boilzCq4VtxbmM2qgf6ag8rUNnTCLuCgEoulGwTn0F5lCrom-7dJOTryW1KI0qUWHMMwl4TX5cLmqJLgBzJapzc5_yEfgQZ9qXzvsT8zeOWSKKPLm7LFVt2Y ihkXa80lWcjewwt61rfQkpmqSzAHL0QIs7CsM9GfnoYc0j9po83-P3GJiBMMFmn-vg" localhost:8080/book/1 -vCopy the code

The response is again forbidden:

 HTTP/1.1 401 Unauthorized
 Content-Length: 0
Copy the code

You may be wondering why this error occurs even after a valid token is provided. If we examine the console for the book service, the following exception is thrown:

org.jboss.resteasy.client.exception.ResteasyWebApplicationException: Unknown error, status code 401 at org.jboss.resteasy.client.exception.WebApplicationExceptionWrapper.wrap( at org.jboss.resteasy.microprofile.client.DefaultResponseExceptionMapper.toThrowable( )Copy the code

This exception occurs because we have been authenticated and have access to the book service, but the bearer token has not been propagated to the rating service.

In order to automatically propagate the Authorization header from the incoming request to the remaining client requests, two changes are required.

The first change is to modify the Rest Client interface, and use annotations to org. The eclipse microprofile. Rest. Client. Inject. RegisterClientHeaders.

public interface RatingService {}
Copy the code

The second change is to configure which headers are propagated between requests. This is set in the file:
Copy the code

Using the same curl command as before, we get the correct output:

 HTTP/1.1 200 OK
 Content-Length: 39
 Content-Type: application/json

* Connection #0 to host localhost left intact
{"bookId":2,"name":"Book 2","rating":1}* Closing connection 0
Copy the code

The elastic

In a microservice architecture, it is important to have fault tolerance so that faults do not propagate from one service to all direct and indirect callers of that service. Quarkus integrates the MicroProfile Fault Tolerance specification with the following annotations for handling failures:

● @timeout: Defines the maximum time to execute before throwing an exception. ● @retry: If the invocation fails, try again. ● @Bulkhead: Limit concurrent execution so that failures in this zone do not overload the entire system. ● @circuitbreaker: Fast failover will occur automatically when execution repeatedly fails. ● @fallback: Provides an alternate solution/default value when execution fails.

Let's add three retries with a delay timer of one second between retries in case an error occurs when accessing the rating service.

@Retry(maxRetries = 3, delay = 1000)
Rate getRate(@PathParam("bookId") Long bookId);
Copy the code

Now, stop the rating service and execute the request. Raise the following exception:

org.jboss.resteasy.spi.UnhandledException: RESTEASY004655: Unable to invoke request: org.apache.http.conn.HttpHostConnectException: Connect to localhost: 9090 / localhost / localhost / 0:0:0:0:0:0:1-0] failed: Connection refusedCopy the code

Obviously, there was an error, but notice that since three retries were performed (with a delay of one second), three seconds passed before the exception was thrown.

In this case, the rating service is down and therefore cannot be restored, but in a real-world example, the rating service may be restored only for a short time, or multiple copies of the service may be deployed, so a simple retry operation may be sufficient to recover and provide a valid response.

However, when there are not enough retries when an exception is thrown, we can either propagate the error to the caller or provide alternative values for the call. This choice can be a call to another system (that is, a distributed cache) or a static value.

For this use case, a rating value of 0 is returned when the connection to the rating service fails.

In order to realize the fallback logic, the first thing to do is to achieve the org. Eclipse microprofile. Faulttolerance. FallbackHandler return type is set to and the fallback strategy methods provide replacement of the same type of interface. In this case, return the Rate to the default object.

import org.eclipse.microprofile.faulttolerance.ExecutionContext;
import org.eclipse.microprofile.faulttolerance.FallbackHandler;

public class RatingServiceFallback implements FallbackHandlerRate {

   public Rate handle(ExecutionContext context) {
       Rate rate = new Rate();
       rate.rate = 0;
       returnrate; }}Copy the code

The last thing to do is to use annotations to getRating @ org () method. The eclipse microprofile. Faulttolerance. Fallback annotation to configure cannot recover back to class.

@Retry(maxRetries = 3, delay = 1000)
Rate getRate(@PathParam("bookId") Long bookId);
Copy the code

If the same request is repeated as before, no exceptions are thrown, but the output of valid values sets the rating field to 0.

* Connection #0 to host localhost left intact
{"bookId":2,"name":"Book 2","rating":0}* Closing connection 0
Copy the code

Any other policy provided by the specification can use the same approach. For example, for circuit breaker mode:

@circuitbreaker (requestVolumeThreshold = 4, failureRatio=0.75, delay = 1000)
Copy the code

If three (4 x 0.75) failures occur in four consecutive called rolling Windows, the circuit will disconnect for 1000 ms and then revert to a half-disconnected state. If the call succeeds at half-open, it is turned off again. Otherwise, it will remain open


In the microservices architecture, it is recommended that the logs of all services be collected in a unified log for efficient use and understanding.

One solution is to use Fluentd, an open source data collector in Kubernetes for a unified logging layer. Quarkus integrates with Fluentd using the Graylog Extended Log Format (GELF).

Integration is really simple. First, use logging logic like any other Quarkus application:

import org.jboss.logging.Logger;

private static final Logger LOG = Logger.getLogger(BookResource.class);

public Book book(@PathParam("bookId") Long bookId) {"Get Book");
Copy the code

Next, enable the GELF format and set the Fluentd server location:

Copy the code

Finally, we can make a request to the endpoint of the record:

curl -H "Authorization: Bearer ..." localhost:8080/book/1

{"bookId":1,"name":"Book 1","rating":3}
Copy the code

Nothing changes in the output, but the log lines are transferred to Fluentd. If we use Kibana to visualize the data, we will see the stored log lines:


Monitoring is another "microservicilitie" that needs to be implemented in our microservice architecture. Quarkus integrates with Micrometer for application monitoring. Micrometer provides a single entry point to the most popular monitoring systems, allowing you to instrument your JVM-based application code without vendor lock-in.

For this example, Prometheus format is used as monitoring output but Micrometer (and Quarkus) also supports other formats like Azure Monitor, Stackdriver, SignalFx, StatsD, and DataDog.

You can register the following Maven dependency to provide Prometheus Output: Monitoring is another "microservices feature" that needs to be implemented in our microservices architecture. Quarkus is integrated with Micrometer for application monitoring. Micrometer provides a single entry point to the most popular monitoring systems, allowing you to detect JVM-based application code without vendor lock-in.

For this example, the Monitor output is in Prometheus format, but Micrometer (and Quarkus) also support other formats such as Azure Monitor, Stackdriver, SignalFx, StatsD, and DataDog.

You can register the following Maven dependencies to provide Prometheus output:

Copy the code

By default, the Micrometer extension registers metrics related to the system, JVM, or HTTP. A subset of the metrics collected are available at the/Q /metrics endpoint, as follows:

The curl localhost: 8080 / q/metrics jvm_threads_states_threads {state = "runnable,"} 22.0 Jvm_threads_states_threads 0.0 jvm_threads_states_threads {state = "blocked",} {} state = "waiting", 10.0 Http_server_bytes_read_count http_server_bytes_read_sum 0.0 1.0Copy the code

However, you can also use the Micrometer API to implement application-specific metrics. Let's implement a custom metric that measures the most highly rated books.

Use IO. Micrometer. Core. Instrument. MeterRegistry class can complete index (in this case for gauge) registration.

private final MeterRegistry registry;
private final LongAccumulator highestRating = new LongAccumulator(Long::max, 0);
public BookResource(MeterRegistry registry) {
    this.registry = registry;
Copy the code

Make a request and verify that the gauge is updated correctly.

curl -H "Authorization: Bearer ..." localhost:8080/book/1

{"bookId":1,"name":"Book 1","rating":3}

curl localhost:8080/q/metrics

# HELP book_rating_max
# TYPE book_rating_max gaugeBook_rating_max 3.0Copy the code

We can also set a timer to record the time it takes to get the rating information from the rating service.

SupplierRate rateSupplier = () - {
      return ratingService.getRate(bookId);
final Rate rate = registry.timer("book.rating.test").wrap(rateSupplier).get();
Copy the code

Ask and verify the time it takes to collect reviews.

# HELP book_rating_test_seconds
# TYPE book_rating_test_seconds summary
book_rating_test_seconds_count 4.0
book_rating_test_seconds_sum 1.05489108
# HELP book_rating_test_seconds_max
# TYPE book_rating_test_seconds_max gauge
book_rating_test_seconds_max 1.018622001
Copy the code

Micrometer uses the MeterFilter instance to customize the measures emitted by the MeterRegistry instance. The Micrometer extension detects the MeterFilter CDI Beans and uses them when initializing the MeterRegistry instance.

For example, we can define a generic tag to set up the environment (production, test, pre-release, and so on) in which the application will run.

public class MicrometerCustomConfiguration {
   public MeterFilter configureAllRegistries(a) {
       return MeterFilter.commonTags(Arrays.asList(
               Tag.of("env"."prod"))); }}Copy the code

Send a new request and verify that metrics are marked.

http_client_requests_seconds_max{clientName="localhost",env="prod",method="GET",outcome="SUCCESS",status="200",uri="/rat E / 2 ", 0.0}Copy the code

Notice that env contains the tag with the value prod.


Quarkus applications leverage the OpenTracing specification to provide distributed tracing for interactive Web applications.

Let's configure OpenTracing to connect to the Jaeger server and set book-service to the service name to identify tracing:

Copy the code

Now send a request:

curl -H "Authorization: Bearer ..." localhost:8080/book/1 {"bookId":1,"name":"Book 1","rating":3}

Copy the code

Access the Jaeger UI to verify that the call is traced:


Developing and implementing a microservice architecture is more challenging than developing an overall application. We believe that microservices can drive you to properly develop services based on your application infrastructure.

Most of the microservices described here (with the exception of apis and pipes) are new or implemented differently in the overall application. The reason is that the application is now broken down into parts, all of which are interconnected in the network.

If you are going to develop microservices and deploy them to Kubernetes on May 26, 2021, Quarkus is a good solution because it integrates smoothly with Kubernetes. Implementing most microservices is simple and requires only a few lines of code.

The source code demonstrated in this article is available on Github.

About the author

Alex Soto is director of developer experience at Red Hat. He is passionate about the Java world, software automation, and he believes in the open source software model. Grim is Manning, co-author of the micro service | testing Java and O 'Reilly Quarkus Cookbook and several open source project contributors. A Java Champion since 2017, he is also an international speaker and teacher at Salle URL University. You can follow him on Twitter (Alex Soto) to keep up with what's happening in Kubernetes and the Java world.

The article is uniformly published in the public number cloud native refers to north

About (Moment For Technology) is a global community with thousands techies from across the global hang out!Passionate technologists, be it gadget freaks, tech enthusiasts, coders, technopreneurs, or CIOs, you would find them all here.