spring

Spring Boot REST API Timeout (with Examples)

Timeouts in REST APIs happen when an API exceeds the anticipated or permitted duration for completion within a Spring Boot application. Typically, there are two categories of timeouts: connection timeouts and read timeouts. Managing these timeouts is crucial to prevent clients from waiting indefinitely for a response. Let us delve into understanding REST API timeout in Spring Boot using practical examples.

1. Timeout a REST API with Spring MVC

Timeouts are essential for preventing long-running requests from causing performance issues or blocking server resources indefinitely.

1.1 Configure Timeout Properties

First, configure timeout properties in your Spring Boot application’s configuration file (e.g., application.properties or application.yml). You can specify the connection and read timeouts in milliseconds:

# application.properties
server.connection-timeout=5000
server.read-timeout=5000

1.2 Implement REST Controller

Create a REST controller with an endpoint that performs the desired operation. For demonstration purposes, we’ll create an endpoint that simulates a time-consuming task:

package com.jcg.example; 

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TimeoutController {

@GetMapping("/timeout")
public String timeoutDemo() throws InterruptedException {
  // Simulate a time-consuming task
  Thread.sleep(7000); // Simulate 7 seconds of processing time
  return "Task completed successfully!";
}
}

1.3 Handle Timeout Exceptions

Configure exception handling to catch timeouts and return an appropriate response to the client:

package com.jcg.example; 

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.concurrent.TimeoutException;

@ControllerAdvice
public class TimeoutExceptionHandler {

@ExceptionHandler(TimeoutException.class)
@ResponseBody
public String handleTimeoutException() {
  return "Request timed out. Please try again later.";
}
}

1.4 Test the API

Test the API by requesting the endpoint and observing the behavior. If the request exceeds the configured timeout, it should return the appropriate response indicating a timeout.

2. Timeout a REST API with Resilience4j

Resilience4j provides a comprehensive set of resilience patterns, including timeout, to improve the fault tolerance of your application.

2.1 Add Resilience4j Dependencies

First, add the necessary dependencies to your project’s build configuration. For Maven, you can include the following dependencies in your pom.xml:

<dependency>
  <groupId>io.github.resilience4j</groupId>
  <artifactId>resilience4j-spring-boot2</artifactId>
  <version>1.7.0</version>
</dependency>

2.2 Configure Timeout Settings

Configure timeout settings for your REST API calls using Resilience4j. You can set the timeout duration in milliseconds:

resilience4j.timeout.instances.default.timeout-duration=5000ms

2.3 Create a CircuitBreakerRegistry Bean

In Resilience4j, a CircuitBreaker is a state machine that monitors the health of a system or a particular service by tracking the number of failures that occur within a given time frame. It’s designed to prevent an application from repeatedly trying to execute an operation that’s likely to fail, thus conserving resources and preventing further degradation of the system.

CircuitBreakerConfig is a configuration class in Resilience4j used to define the behavior and characteristics of a CircuitBreaker instance. It allows developers to fine-tune how CircuitBreaker operates based on their application’s requirements and failure-handling strategies.

Create a CircuitBreakerRegistry bean to manage circuit breakers:

package com.jcg.example; 

import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ResilienceConfig {

  @Bean
  public CircuitBreakerRegistry circuitBreakerRegistry() {
	  return CircuitBreakerRegistry.of(
		  CircuitBreakerConfig.custom()
			  .slidingWindowSize(5)
			  .permittedNumberOfCallsInHalfOpenState(3)
			  .waitDurationInOpenState(Duration.ofMillis(1000))
			  .build()
	  );
  }
}

2.4 Use the Timeout Decorator

Apply the timeout decorator to your REST API calls using Resilience4j annotations:

package com.jcg.example; 

import io.github.resilience4j.timelimiter.annotation.TimeLimiter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TimeoutController {

  @TimeLimiter(name = "timeoutDemo")
  @GetMapping("/timeout")
  public String timeoutDemo() throws InterruptedException {
	  // Simulate a time-consuming task
	  Thread.sleep(7000); // Simulate 7 seconds of processing time
	  return "Task completed successfully!";
  }
}

2.5 Test the API

Test the API by requesting the endpoint. If the request exceeds the configured timeout, Resilience4j will handle it and return an appropriate response.

3. Handling Timeouts in Java-Based REST APIs

When building Java-based REST APIs, handling timeouts is crucial to ensure the reliability and responsiveness of your application.

3.1 Timeout a Long-Running Database Operation using @Transactional Annotation

In some cases, database operations may take longer than expected, leading to performance issues or resource contention. To prevent these problems, it’s essential to set a timeout for such operations. In Java, you can achieve this using the @Transactional annotation along with specific configuration.

3.1.1 Configure Transaction Timeout

First, define the timeout value for your transactional method using the timeout attribute of the @Transactionalannotation. The timeout value is specified in seconds:

package com.jcg.example; 

import org.springframework.transaction.annotation.Transactional;

@Service
public class MyService {

  @Autowired
  private MyRepository myRepository;

  @Transactional(timeout = 30) // Timeout set to 30 seconds
  public void longRunningOperation() {
	  // Perform a long-running database operation
	  myRepository.doLongRunningTask();
  }
}

3.1.2 Handle Timeout Exception

When the transaction exceeds the specified timeout, a TransactionTimedOutException will be thrown. You can handle this exception and take appropriate action, such as logging the timeout or rolling back the transaction:

package com.jcg.example; 

import org.springframework.transaction.TransactionTimedOutException;
import org.springframework.transaction.annotation.Transactional;

@Service
public class MyService {

  @Autowired
  private MyRepository myRepository;

  @Transactional(timeout = 30) // Timeout set to 30 seconds
  public void longRunningOperation() {
	  try {
		  // Perform a long-running database operation
		  myRepository.doLongRunningTask();
	  } catch (TransactionTimedOutException e) {
		  // Handle timeout exception
		  // Log the timeout
		  System.out.println("Database operation timed out.");
		  // Rollback the transaction or perform other actions as needed
	  }
  }
}

3.2 Timeout a Remote API Call with RestTemplate or WebClient

When making remote API calls in a Java application, it’s important to handle timeouts to prevent blocking and resource contention. In this article, we’ll explore how to implement timeout functionality using both RestTemplate and WebClient, which are commonly used in Spring applications for consuming RESTful services.

3.2.1 Using RestTemplate

With RestTemplate, you can set connection and read timeouts using the ClientHttpRequestFactory. Here’s how to configure timeouts:

import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

// Create RestTemplate instance
RestTemplate restTemplate = new RestTemplate();

// Set connection and read timeouts
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(5000); // 5 seconds
factory.setReadTimeout(5000); // 5 seconds
restTemplate.setRequestFactory(factory);

// Make remote API call
String response = restTemplate.getForObject("https://example.com/api/resource", String.class);

3.2.2 Using WebClient

WebClient is a non-blocking, reactive HTTP client introduced in Spring WebFlux. To set timeouts with WebClient, you can use the timeout operator:

import org.springframework.web.reactive.function.client.WebClient;
import java.time.Duration;

// Create WebClient instance
WebClient webClient = WebClient.create();

// Make remote API call with timeout
String response = webClient.get()
		.uri("https://example.com/api/resource")
		.retrieve()
		.bodyToMono(String.class)
		.timeout(Duration.ofSeconds(5)) // 5 seconds
		.block();

3.3 Alternative Options

While Resilience4j is a popular choice for managing timeouts, there are several alternative options available, each with its strengths and use cases.

Yatin

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button