Scaling Your Java MicroProfile-Based Microservices App (3 of 3)

5 min read

By: David Shi

MicroProfile simplifies the handling of service faults to minimize their impact

Just as life often takes unpredictable turns, running and maintaining an application can can often be an uncertain process. And sometimes even the smallest of errors can propagate into the largest of problems (especially in a microservice architecture in a distributed systems environment) if you don’t plan for fault tolerance. On a smaller scale, a team of system managers could manually handle debugging and maintaining multiple services over multiple machines, but with container orchestration emerging as an increasingly popular modern DevOps tool, we can automate work more efficiently and support scaling upwards without hassle.

Our previous blog post covered the tools to implement effective security on your Java EE application. In this blog post, as the final part of the Scaling Your Java MicroProfile-Based Microservices App series, we will cover the basics of implementing fault tolerance using two open source solutions: Hystrix and MicroProfile Fault Tolerance.

MicroProfile

To demonstrate and highlight the pros and cons between these two technologies, we’ll use IBM’s BlueCompute storefront application on GitHub. This repository hosts a Spring Boot implementation that uses Hystrix and, more recently, an Open Liberty version that implements MicroProfile and features MP Fault Tolerance. Today’s blog will not only seek to explore the differences between implementing fault tolerance with MicroProfile and Hystrix but will also include helpful developer notes.

The BlueCompute store has its core microservices divided into Inventory, Orders, Customer, and Catalog as depicted in a Kubernetes cluster hosted in an IBM Cloud Private environment:

Ingress Controller

In the next two sections, we’ll compare and contrast the fault tolerance implementations for the Customer microservice for Spring Boot and MicroProfile.

Spring Boot: Fault tolerance with Hystrix

In the Spring Boot implementation, the first step is enabling fault tolerance. This is done by enabling a circuit breaker pattern with a simple annotation:

@SpringBootApplication
@EnableCircuitBreaker
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(application.class, args);
    }
}

The @EnableCircuitBreaker annotation notifies Spring to watch over our application with Hystrix for faults. The faults that can trigger the circuit breaker are defined in Hystrix properties, such as defining a volume threshold or error percentage. Using HystricCommandProperties, we would call circuitBreakerRequestVolumeThreshold() or circuitBreakerErrorThresholdPercentage()respectively. Although useful, the actual developer specifics follow predefined coding templates. The code excerpt below shows the most basic forms of error handling with how Hystrix goes to a fallback method from a timeout in our Customer microservice:

// CustomerController.java

@HystrixCommand(fallbackMethod = "fallbackService")
@RequestMapping(value = "customer", method = RequestMethod.GET)
RequestEntity<?> getCustomer() {
    HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter();
    commandProperties.withExecutionTimeoutInMilliseconds(2_000);

    final String customerId = getCustomerId();
    final Customer cust = getCloudantDatabase().find(Customer.class, customerId);
    return ResponseEntity.ok(Arrays.asList(cust));
}

private ResponseEntity<?> fallbackService() {
    System.out.println("Oh no!");
    return ResponseEntity.status(500).entity("Something went wrong.").build();
}

Let’s look at this code more closely.

The first method, getCustomer, provides the actual customer data. The second method, aptly named fallbackService, provides an alternative course of action if anything goes wrong. For simplicity, the above fallbackService simply logs to sysout and returns a server 500 error code. The getCustomer method specifies this handling thanks to the @HystrixCommand annotation with the fallback parameter.

As a reminder, the fallback method in production-grade code should do whatever is necessary to fail application gracefully. Needless to say, even printing a stack trace or returning a reasonable default would save you more trouble than printing an “Oh no!”

Execution jumps to the fallback method, defined by the @HystrixCommand annotation and parameter, whenever one of the specified faults occurs, such as this case of whenever the execution time exceeded 2000 milliseconds/2 seconds. Choosing such a duration stems from the fact that this method invokes the API of a Cloudant database, which operates at the millisecond range. Being mindful of your operations’ normal behaviors will allow you to better estimate a reasonable window of time to detect something odd. Estimate too low and your application will return false positives; estimate too high and you risk failing user’s expectations of app responsiveness. Choosing your own sensible buffer time is a judgement call. Note that Hystrix doesn’t implement a retry functionality directly, so you would instead have to work with SpringRetry.

In addition to including various other ways to set up your own trigger cases, Hystrix also comes with a simple way to set up a remote container for testing your error handling net. For example, in a test case, you can force a failure by simply setting up the execution of the method to hang longer than the time allotted. For reference, you can check out Baeldung’s introduction to Hystrix.

MicroProfile: Fault tolerance

When I began reading up on MicroProfile, it became immediately apparent that the design usage differed between both fault tolerance libraries. Hystrix seems more driven by developer templates and clarity in code to monitor and manage recovery, whereas MicroProfile’s seeks to separate the execution logic from execution and does so with descriptive headers.

In the BlueCompute store app, getCustomer looks a little different after using MicroProfile Rest Client, but you’ll find that it performs in the same spirit. Authentication is slightly different with a Json Web Token, obtained through CDI, but we still get a Cloudant database and then search through it by a username.

@Inject
private JsonWebToken jwt;

public Response getCustomerByUsername() throws InterruptedException {
    String username = "usernames:" + jwt.getName();
    return CloudantClient.getCustomerByUsername(username);
}

That’s the minimum, but let’s decorate it the way we did in the earlier Spring Boot version:

@Inject
private JsonWebToken jwt;
@Timeout(value = 2, unit = ChronoUnit.SECONDS)
@Retry(maxRetries = 2)
@Fallback(fallbackMethod = "fallbackService")
public Response getCustomerByUsername() {
    String username = "usernames:" + jwt.getName();
    return CloudantClient.getCustomerByUsername(username);
}


public Response fallbackService() {
    System.out.println("Oh no!");
    return Response.status(500).entity("Something went wrong.").build();
}

To me, this looks more intuitive than the Spring Boot/Hystrix code excerpt. You specify the timeout, retry, and fallback in one place, each defined by an annotation. Separating the execution logic from the execution allows us to attach MicroProfile Fault Tolerance without altering any code—in many cases, you really don’t even need to know what the fault tolerant code does. In other words, this level of abstraction focuses on enabling fault tolerance at a higher level.

All in all, the transition felt super painless. The migration of error handling translated very well, and tacking those onto your methods couldn’t get any simpler. To find further reading on MicroProfile Fault Tolerance or any other MicroProfile features, OpenLiberty.io provides user-friendly demos, guides, and documentation.

Conclusion

To summarize, I find both technologies to be equally powerful and equally valid features to attach to your Java EE application. They do very similar things but in a widely different flavor. Hystrix uses coding templates to set up and frame their error handling, and it appeals to developers that favor code over declarations. MicroProfile uses headers and annotations to simplify the developing experience without sacrificing performance.

As a developer, I felt that the time I spent from reading up on fault tolerance to implementing it with MicroProfile was faster than doing that with Spring. Because not only was putting it in the code simpler, I had confidence from the guides and the general intuitiveness to support me. With just a quick peek at this blog, you’ve learned the basics of coding with MicroProfile.

open liberty hero

For a deeper and more specific interactive experience with a feature of choice, Open Liberty does host some great guides, such as this one on fault tolerance. Setting up Hystrix took a little extra effort, but without a doubt has the potential to become more powerful over time.

This blog only covers a couple of features in the MicroProfile suite, so be sure to visit the other blog posts that cover more sections. The code snippets shown were simplified versions of the code, available on GitHub. Check out how you can also host this app onto IBM Cloud and IBM Cloud Private.

Be the first to hear about news, product updates, and innovation from IBM Cloud