This is the final of a series of 3 articles dedicated to the MicroProfile Fault Tolerance. We started by introducing and giving an overview of the specification in “MicroProfile Fault Tolerance, Take 2”. Next, we explained the different annotations and their options in “MicroProfile Fault Tolerance Annotations”.
Now we take a look at practical MicroProfile Fault Tolerance examples using TomEE 7.1. This version of TomEE is compliant with MicroProfile v1.2 and includes the Geronimo Safegard library implementing the Fault Tolerance v1.0 spec.
The MicroProfile Samples project
We’ll be using the microprofile-samples project created by Roberto Cortez and which is available on Github as microprofile-samples. The purpose of the microprofile-samples project is to provide simple working examples that you can execute to learn more about the spec and to get inspiration for your own projects. It’s open source and Apache 2 licensed.
To run the examples, we assume that you already have installed on your computer the following software:
- Git command line tool
- Java JDK 8+
- Maven build tool
- The Curl command line tool is optional but handy.
Let’s start by cloning the microprofile-samples
project using this git command:
git clone https://github.com/tomitribe/microprofile-samples.git
Now that you have cloned the repository, you can import it to your IDE or build it with Maven using the following commands:
cd microprofile-samples
mvn clean install
Inside the microprofile-samples
project, we include the arquillian, the MicroProfile Config and the Fault Tolerance sub-projects.
The arquillian sub-project was created to simplify the test classes by not requiring a deployment folder. It will simply use the built artifacts from each sample sub-project.
Let’s go to the Fault tolerance sample:
cd fault-tolerance
ls -al
There are a few sub-projects, in alphabetical order:
- bulkhead
- circuit-breaker
- fallback
- retry
- timeout
- timeout-retry-fallback
Each sub-project exists to demonstrate a particular part of the spec.
All sub-projects have a similar structure. Using the timeout
as a reference, we see the main source code containing the ApplicationBean
class is launched on startup and the MovieResource
class is used to publish the REST endpoints.
The MovieResourceTest
class is the Arquillian integration test that you can use.
All MovieResource<
CDI beans have at least a findAll()
method to return a list of movies. We will use it as a Guinea Pig to demonstrate the behavior of the Fault Tolerance annotations. This is the resource for the Timeout example:
@ApplicationScoped
@Path("/movies")
public class MovieResource {
/**
* After a default timeout of 1 second, the request will fail with TimeoutException.
*
* @return the movies list
*/
@GET
@Timeout
public List findAll() {
...
}
}
Different samples will have different annotations and control logic to trigger faults inside the findAll()
method, but they are all built following this project structure.
Trying out the samples
The sample applications can be executed in 3 different ways:
- Using the Arquillian tests inside each sub-project. You can use your IDE to run each of the examples. Look for the
MovieResourceTest
classes. - The applications are packaged using the WAR file format. Most of them can be deployed by going to a subproject and execute the TomEE Maven plugin as shown below:
cd timeout
mvn clean package -DskipTests tomee:run
- Running a single fat jar that bundles the app and TomEE. If the fat jar is not in the timeout/target folder you need to build the project first using the following commands:
cd timeout
mvn clean package -DskipTestse>
java -jar target/retry-1.0-SNAPSHOT-exec.jar
The Fallback sample
Let’s see in detail a more interesting example, the Fallback. This pattern allows us to declare an alternative implementation to use when something goes wrong. The following is the findAll()
method in the MovieResource
class:
@GET
@CircuitBreaker(failOn = BusinessException.class)
@Fallback(MovieFindAllFallbackHandler.class)
public List findAll() {
throw new BusinessException();
}
As you can see, the method is set up to always fail. When the method fails the circuit-breaker will open the circuit (refusing requests) right away in response to the BusinessException
being thrown. Because we are declaring an alternative execution path, the fallback, the client still gets a response.
In the same package, the MovieFindAllFallbackHandler
provides the alternative execution:
public class MovieFindAllFallbackHandler implements FallbackHandler<List> {
@Override
public List handle(final ExecutionContext context) {
return Stream.of("The Terminator", "The Matrix", "Rambo").collect(toList());
}
}
Let’s run this on TomEE. Go to the project folder .../microprofile-samples/fault-tolerance/fallback/
and build the project:
mvn clean package -DskipTests
Then run the embedded TomEE server with the application:
java -jar target/fallback-1.0-SNAPSHOT-exec.jar
On the log you can spot where the REST endpoints are being published:
TomEE will be running after the Server startup message:
...INFO [main] sun.reflect.DelegatingMethodAccessorImpl.invoke Server startup in 2271 ms
Once TomEE is running you can make some requests. We could use a browser but it would not display the list of movies because we are returning pure a JSON payload. Instead, we will use a command line curl command to get the list of movies:
~$ curl -i http://localhost:8080/fallback-1.0-SNAPSHOT/movies/
HTTP/1.1 200
Date: Tue, 02 Oct 2018 08:06:40 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Server: Apache TomEE
["The Terminator","The Matrix","Rambo"]
As you can see, the error is isolated by the circuit breaker and the fallback is giving us the response.
The timeout-retry-fallback sample
You might be thinking “this fancy fallback is nothing more than a glorified catch from a try block”. In a sense it is, but it’s much more powerful…
Take the timeout-retry-fallback
sub-project:
@GET
@Timeout // default 1 sec.
@Retry(delay = 1000, maxRetries = 2)
@Fallback(MovieFindAllFallbackHandler.class)
public List findAll() {
// operation taking forever to complete.
}
In here, we can very easily wait for an operation to complete if there is a timeout. When timeouts happen, we can retry twice, with a small delay between calls. If, in the end, the request is not successful, the fallback will be invoked.
Try out the other patterns, with them you can precisely control the execution flow and also keep things tidy. You will need less code to perform these operations and that also means fewer bugs.
In future versions of TomEE, because Fault Tolerance v.1.1 defines integrations with the Configuration and Monitoring APIs, you will also be able to override the annotation options without the need of a re-deploy and receive metrics out of the box.
Thanks for reading! Feel free to send us a pull request with MicroProfile samples to include in the project. Stay tuned for more MicroProfile posts!