5 design patterns for microservices with Quarkus and MicroProfile

Before getting started, you may want to check my book “5 Steps to an extraordinary career“, which will guide you to build your dream career as a software developer! Click here to check it out.

 

Have you ever had any doubts about how to start your microservices-based application from scratch? Or how to break down your monolith into many microservices?

Usually, the answers to these questions are not unique, not even likely, which is not helpful at all. What brings another question: is there any safe and solid path that I can follow to have success on my microservices projects?

You bet! Microservices have some specific design patterns that can be a life saving for any project, and this post will show you some of the best of them.

If you are much more a visual person (or would like to see all the code here really working), check its related video:

When using microservice for your projects it’s important to do it in the right way and using the right approach, no matter if you are breaking down your monolith or starting a new project using microservice from scratch.

When doing it using design patterns, you’ll be able to not only do it properly but also with less effort.

And of course, there are a lot of design partners for microservices. If you ever checked the content of Chris Richardson (which I recommend 100%) you know that he has mapped something around 40 to 50 microservices patterns. That’s a lot! And so useful.

Today I’ll cover five of them here and they are:

  • Externalized Configurations
  • Circuit Breaker
  • Health Check
  • Distributed Tracing
  • API Metrics

Creating the project

I create my projects using the site: http://code.quarkus.io

If you do the same, configure your project with your preferences and choose these extensions:

  • REST Client
  • SmallRye Fault Tolerance
  • SmallRye Health
  • SmallRye OpenTracing
  • SmallRye Metrics

When you are ready, click the “Generate your application” button, download the file, and unzip it.

Externalized Configuration

This is the code of an endpoint that is using an externalized configuration:


@Path("/config")
public class ConfigResource {
@ConfigProperty(name = "config")
private Optional<String> config;
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getConfig(){
return Response.ok("Hello, " + config.orElse("Optional")).build() ;
}
}

If you compile and execute this code just as it is here, it will print:

Hello, Optional

Because it can’t find the “config” property anywhere. So let’s add it to your applications.properties file (available in the “resources” folder if you created your project as stated in the previous section):


config=Config

Now, if you compile and execute your code again, it will print:

Hello, Config

It’s better because it’s getting the value from a config property, but it’s still not so good because the property it’s still inside the project. So let’s move it to the environment.

If you are running your project using the Quarkus plugin for Maven you probably did something like this:

mvn compile quarkus:dev

So do it instead:

mvn compile quarkus:dev -Dconfig=Quarkus

Now if you execute the code it will print:

Hello, Quarkus

This externalized configuration can be at your container, your server, your cluster, wherever your application is running, It will get the configuration from the environment.

Circuit Breaker

Imagine in your house where you have your electrical circuit where all the outlets and lights and are connected. And the circuit is closed because everything is going well.

But once you have some problem inside the circuit (a short circuit, overload, etc), it will open. That way the energy will not flow inside the circuit anymore, keep this energy problem away from your home appliances.

The same happens when we are using microservices and apply the circuit breaker pattern. Once you have some failing microservice the circuit will open. By doing this it will not start a cascade of failing inside your “circuit” (=your microservices that are communicating to each other). 

For example, if you have a microservice giving a timeout error, the circuit will open and it will return an error right away, with no needing to all incoming requests to wait for the timeout to happen.

So, let’s do it. This is the code of another endpoint that is using a circuit breaker design pattern:


@Path("/circuit")
public class CircuitResource {
@Timeout(unit = ChronoUnit.MILLIS, value = 500)
@Fallback(fallbackMethod = "fallback")
@CircuitBreaker(requestVolumeThreshold = 4, failureRatio = 0.5, delay = 2000, delayUnit = ChronoUnit.MILLIS, successThreshold = 2)
@GET
public String getCircuit() throws InterruptedException {
Thread.sleep(600);
return "Circuit \n";
}
private String fallback(){
return "I'm a fallback ";
}
}

The proposal here is to show you how to design a microservice using a circuit breaker pattern. So we have a service that is returning a timeout exception. Let’s break it down for better understanding:

  • @Timeout – defines a timeout limit for this microservice
  • @Fallback – defines a method to be used in case of failure (note that the method is implemented at the end of the code with the name “fallback”)
  • @CircuitBreaker – defines that on every 4 requests (requestVolumeThreshold), if we have 50% of them failing (failureRatio) the circuit will open. Then it should wait 2 seconds (delay) and try to close back the circuit. If it has 2 requests that perform successfully (successThreshold) it will keep closed. Otherwise, it will open and try again after 2 seconds (delay). 

So imagine that you have thousands of requests coming to this microservice and getting this exception. Without this circuit breaker implementation, all those requests would wait until timeout unnecessarily, which would create a huge queue of requests waiting for a response that will not come. That’s when a cascade failure happens.

By giving an exception right away you give the requester service to handle it properly and faster.

Health Check API

One of the key aspects of the Cloud Native approach is that your service should be observable. And one of the features you should implement the health checks probes.

There are two probes that you will implement:

  • Liveness probe: it will “say” if your microservice is alive or not
  • Readiness probe: it will “say” if you microservice is ready to handle requests or not

Usually, your liveness probe won’t have too much complexity, so here it is an example of implementation:


@Liveness
public class LivenessCheck implements HealthCheck {
@Override
public HealthCheckResponse call() {
return HealthCheckResponse.up("I'm alive!");
}
}

So what makes a class to be your liveness probe:

  • To implement the HealthCheck interface
  • Be annotated with @Liveness 

Now to the readiness probe. Imagine that you microservice have some required dependency before start answering requests (access some database, third party service, a message queue, etc); here it is where you should validate if this dependency is available/ready before start handling requests:


@Readiness
public class ReadinessCheck implements HealthCheck {
@Override
public HealthCheckResponse call() {
Client client = ClientBuilder.newClient();
Response response = client.target("https://eldermoraes.com").request().get();
if (response.getStatusInfo().getFamily().equals(Response.Status.Family.SUCCESSFUL)){
return HealthCheckResponse.up("I'm ready!");
} else{
return HealthCheckResponse.down("I'm not ready yet…");
}
}
}

Let’s break this code down:

  • Interface implementation: the same as for liveness (HealthCheck interface)
  • Annotation: @Readiness
  • call() implementation: here I made this probe to validate if my blog (http://eldermoraes.com) is up and running. If so, the readiness will be “up”. If not, will be “down”.

How do these probes work on a Kubernetes cluster?

(I’m assuming that you are writing microservices to be managed in some orchestrated environment and that Kubernetes is the standard option for it today).

  • Liveness probe: if it returns a status code (HTTP) different of 2xx, the node manager will kill the pod (where the container is) and will replace it with a brand new one
  • Readiness probe: if it returns a status code of 2xx, the manager will start sending requests to it. If not, it will hold on until it is… ready! 🙂

Distributed Tracing

This is the easiest one.


@Path("/trace")
public class TraceResource {
@GET
@Traced
public String getTrace(){
return "Trace";
}
}

You just need to annotate the class/method you would like to trace with @Traced. Now you just need some tracing tool (Kiali, Zipkin, etc).

Side note: for me, this is a great example of the advantages of using standards. It will ease your life in many ways.

Metrics API

Here is a class with some metrics being collected:


@Path("/mymetrics")
public class MetricsResource {
@GET
@Path("counted")
@Counted
public String getCounted(){
return "Counted";
}
@GET
@Path("timed")
@Timed
public String getTimed(){
return "Timed";
}
}

Breaking it down:

  • @Counted: will collect statistics related to the frequency of calling to this method
  • @Timed: will collect statistics related to the time consumed by this method

To see it working, just call these methods many times and then call this URL:

http://localhost:8080/metrics/application

That’s it!

I hope it’s useful for you. Remember, when we are using standards, we have a lot of heavy lifting of our coding being reduced by the standard. So that’s why I like MicroProfile, especially when using it with Quarkus, which gives me a lot of productivity with its developer mode (and, of course, its crazy fast startup).

So this combination between MicroProfile and Quarkus is amazing!

If you tried to implement some of these design patterns, leave your comment here and tell me how it was your experience. And if you have any questions, I’ll be more than happy to answer them!

 

1 thought on “5 design patterns for microservices with Quarkus and MicroProfile”

  1. You have covered all the essentials like health checks, tracing, and metrics. It’s like having a microservices toolbox at your fingertips.
    Thank you for sharing !!

Leave a Reply

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