Getting started with MicroProfile FaultTolerance API

The Microprofile Fault Tolerance specification has been specifically built to make your microservices to common failures like network problems or an IOException.

In this tutorial we will learn how to use some patterns like @Timeout, @Retry, @Fallback and @CircuitBreaker policies to drive an alternative result when an execution does not complete successfully.

Let’s start from the @Timeout annotation which prevents from the execution from waiting forever.

Example:

@GET
@Path("/hello")
@Timeout(250)
public String getData()
{
	randomSleep();

	return "Random string " + UUID.randomUUID().toString();
}

You can also add a @Retry and @Fallback policy to refine your fault-tolerance policy.

  • @Retry lets you retry the execution of the method in case of failure, up to a maximum number of retries
  • @Fallback lets you drive the execution to a fallback method, to avoid returning a TimeoutException.
package com.mastertheboss.microprofile.service;

import org.eclipse.microprofile.faulttolerance.Fallback;
import org.eclipse.microprofile.faulttolerance.Timeout;

import javax.annotation.PostConstruct;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import java.util.Random;
import java.util.UUID;

@Path("/")
public class SimpleRESTService {

	String cachedData;

	@PostConstruct
	public void init() {
		cachedData = UUID.randomUUID().toString();
	}
	@GET
	@Path("/hello")
	@Timeout(250)
	@Retry(maxRetries = 2)
	@Fallback(fallbackMethod = "getCachedData")
	public String getData()
	{
		randomSleep();

		return "Random string " + UUID.randomUUID().toString();
	}
	private void randomSleep() {
		try {
			Thread.sleep(new Random().nextInt(400));
		} catch (Exception e) {
            System.out.println("The application is taking too long...");

		}
	}

	public String getCachedData()
	{

		return "Random (cached) String is "+cachedData;
	}
}

Sometimes you might want an application to wait before it issues a retry request. For example, if the failure is caused by a sudden spike in requests or a loss of connectivity, waiting might decrease the chance that a previous failure occurs. In these cases, you can define a delay in the Retry policy using these parameters:

  • delay: The amount of time to wait before retrying a request. The value must be an integer greater than or equal to 0 and be less than the value for maxDuration. The default is 0.
  • delayUnit: The unit of time for the delay parameter as described by the java.time.temporal.ChronoUnit class. The default is ChronoUnit.MILLIS.
  • durationUnit: defines a standard set of date period units, including NANOS, MICROS, MILLIS, SECONDS, MINUTES, and HOURS.
@Retry(retryOn = TimeoutException.class,
       maxRetries = 4,
       maxDuration = 10,
       durationUnit = ChronoUnit.SECONDS,
       delay = 200, delayUnit = ChronoUnit.MILLIS)

Finally, you can specify an Exception class that triggers a retry. You can identify more than one exception as an array of values. For example,

@Retry(retryOn = {RuntimeException.class, TimeoutException.class}).

The default is java.lang.Exception.class.

Using a Circuit Breaker Policy

A Circuit Breaker policy can be used to prevent repeated failures when a service is rated as not functional. There are three circuit states:

  • Closed: In normal conditions, the circuit is closed. In case of failure occurs, the Circuit Breaker tracks the event and the requestVolumeThreshold and failureRatio parameters determine the conditions under which the breaker will transition the circuit to open.
  • Open: When the circuit is open, any call to the service will fail immediately. A delay may be configured for the Circuit Breaker. After the delay, the circuit transitions to half-open state.
  • Half-open: In half-open state, by default one trial call to the service is allowed. Should it fail, the circuit will return to open state. You can configure the number of trials with the successThreshold parameter. After the specified number of successful executions, the circuit will be closed.

A method or a class can be annotated with @CircuitBreaker, as in this example:

@CircuitBreaker(successThreshold = 10, requestVolumeThreshold = 4, failureRatio=0.75, delay = 1000)
public Service checkBalance() {
    counterForInvokingService++;
    return checkBalanceService();
}

The above code excerpt applies the CircuitBreaker policy, which is to open the circuit once 3 (4×0.75) failures occur among the rolling window of 4 consecutive invocation. The circuit will stay open for 1000ms and then back to half open. After 10 consecutive successful invocations, the circuit will be back to close again.

Setting up projects with FaultTolerance API

There are several platforms which are compatible with FaultTolearance API. In particular you can run the above examples in any of the following platforms:

Thorntail:

Include the following dependencies:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.thorntail</groupId>
            <artifactId>bom-all</artifactId>
            <version>${version.thorntail}</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependency>
    <groupId>io.thorntail</groupId>
    <artifactId>microprofile-fault-tolerance</artifactId>
</dependency>

Quarkus:

Include the following dependencies:

<dependencyManagement>
  <dependencies>
	  <dependency>
	    <groupId>io.quarkus</groupId>
	    <artifactId>quarkus-bom</artifactId>
	    <version>${quarkus.version}</version>
	    <type>pom</type>
	    <scope>import</scope>
	  </dependency>
  </dependencies>
</dependencyManagement>

<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-smallrye-fault-tolerance</artifactId>
</dependency>

Tom EE:

<dependency>
	<groupId>org.eclipse.microprofile</groupId>
	<artifactId>microprofile</artifactId>
	<version>${version.microprofile}</version>
	<type>pom</type>
	<scope>provided</scope>
</dependency>

Source code for this tutorial: https://github.com/fmarchioni/mastertheboss/tree/master/micro-services/mp-fault-tolerance


Recommended Articles

Handling Fault Tolerance in Enterprise Java Applications with Microprofile

Learn how to implement fault tolerance in your enterprise applications using Microprofile's fault tolerance specification and Thorntail runtime.

Build and Deploy Microprofile Applications on Wildfly with MicroProfile API Support

Learn how to create and deploy Microprofile applications on Wildfly with the latest version of Microprofile API support, combining Jakarta EE and MicroProfile APIs.

Configuring Microservices with WildFly and MicroProfile Configuration

Learn how to externalize and inject configuration properties for your microservices using WildFly and the MicroProfile Configuration API.

Configure MicroProfile API for Enterprise Java Applications using Thorntail or Quarkus

Learn how to use Microprofile API in Java EE applications with Thorntail and Quarkus, including dependency management and health checks.