Blog

A common Pitfall of Spring Boot's RestTemplate

A common Pitfall of Spring Boot's RestTemplate

A common Pitfall of Spring Boot's RestTemplate

Spring
Development
Spring
Development

17.12.2021

by

-

3 min

read

One of the most fundamental advantages of Spring Boot is the paradigm of convention over configuration reducing the overhead heavily. Even so, there is a tiny pitfall in the default behavior of Spring Boot's RestTemplateBuilder that is probably not what you are really aiming for. So, be sure that you have configured it manually.

Set the Stage: Use Defaults

So what is the problem with the RestTemplateBuilder? To expose this, we need to have two simple applications that communicate with each other over HTTP. In this blog post, we use a simple demo online shop that consists of a Gateway microservice requesting an endpoint of another microservice (called Hotdeals) over HTTP. The following listing shows the relevant parts of the code; you can check the full source code of the demo online shop in the GitHub repository.

@RestController
@RequestMapping("/products")
public class ProductsController {
    private final RestTemplate restTemplate;
    @Value("${rest.endpoint.hotdeals}")
    private String urlHotDeals;
    ...
    public ProductsController(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @GetMapping
    private List<Product> getHotDealsProduct() {
        return this.restTemplate.exchange(
            this.urlHotDeals,
            HttpMethod.GET,
            null,
            this.productListTypeReference
        ).getBody();
    }
}

While this code works fine under good conditions, the problem comes to light under turbulent conditions - for example, network problems such as network package loss. So how can we verify what will happen? Without changing the code or doing too much low-level network stuff! This is a problem Steadybit can help with using the Chaos Engineering Engine in an experiment!

Uncover the Pitfall Test Driven

Since Steadybit has already discovered our application, we can easily create experiments to check the behavior. We will simulate dropping network packages for incoming and outgoing connections for Hot-Deals. Just follow the next steps to create and run the experiment. If you have not yet installed and configured Steadybit, request your demo to discuss the next steps.


Step 1: Create an Infrastructure Experiment

First, you need to create and define a new experiment. When using steadybit, this is quite simple:

  1. We go to experiments and select a new infrastructure experiment (because we deal with the network).

  2. We give the experiment a meaningful name (e.g., "Gateway's HTTP communication is robust") and choose the “Global” area (providing you full access to all discovered targets).

  3. We decide to attack containers and specify them with attributes. The following screenshot shows the query we used to narrow down the targets to the microservice Hot-Deals.

  1. Since we want to achieve a maximum effect, we choose an impact of 100% in the following step of the wizard.

  2. Apply the "blackhole" attack from the "network" category to disrupt incoming and outgoing network connections. We can keep the default settings of the attack.

  3. We skip the "Execution and Monitoring" part and create the experiment by clicking Save experiment.

Step 2: Check System's Behavior

Now it's time to check what we forgot in our implementation. Run the experiment you created and see what happens in the application by checking the Network tab.

Once the blackhole attack runs, the product list update is halted and remains pending (last line). The request is neither resolved nor automatically canceled for the entire experiment duration (30 seconds). Why is this? At least there should be some timeouts, right?

Step 3: Fix the Pitfall

As mentioned earlier, Spring Boot already has many useful defaults that do not require additional configuration, but unfortunately, they are missing some for timeout configurations of RestTemplate or RestTemplateBuilder. We correct the initialization of the container's RestTemplate using the following code to fix this.

@Bean
public RestTemplate restTemplate() {
    return new RestTemplateBuilder()
        .setConnectTimeout(Duration.ofSeconds(2))
        .setReadTimeout(Duration.ofSeconds(2))
        .build();
}

Step 4: Verify the Fix

Verifying the new implementation by re-running the experiment shows that the application waits for the endpoint's response for about 2 seconds and then terminates with an exception. This exception is caught and properly handled, as we verified in an earlier blog post.

Conclusion

In this blog post, we tested and improved handling a synchronous HTTP request under turbulent conditions. Using Steadybit, we proved that we cannot always rely on the default Spring Boot configurations and needed to adjust them. This verification was possible without changing the source code or low-level networking. It also shows the importance of thinking about turbulent conditions when testing your code - even when using widely established frameworks and reasonable conventions.