Monday, March 27, 2023

How to setup Request timeout in Spring Boot REST API? Example Tutorial

Hello guys, if you are wondering how to setup request timeout in your REST API using Spring boot then you have come to the right place. First thing first, when you make a request to another API in production, you must add timeout as there is no guarantee that you will receive response in time. Today we are going to take a look at setting up request timeout in Spring Boot Rest API. Before making it into the topic. Let’s first understand a few terms here. What is a Request timeout? A Request timeout is a deadline for services to respond to requests. It ended up returning an error with status 504 if the request fail to respond within the specified time. Request timeouts can help prevent a bad user experience, especially when a request is taking a long to respond to. There are various ways to set request timeout in Spring Boot

Let’s go through a few of these approaches.

Timeout using @Transactional annotation

To set request timeout on database queries or calls by using Spring's @Transactional annotation. We may set the timeout attribute that it has. It has a default value of -1, which is the same as having no timeout at all.

Let's say we have set this timeout to 50 seconds. An exception will be raised if the annotated method takes longer than this amount of time to execute. This approach is quite handy for time-consuming database searches or long-running SQL queries.

Let’s see it in action, we will create a simple rest endpoint that returns the student's details as shown below.

Student.java

@Entity
@Table(name = "students")
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)

    private int id;
    private int age;
    private String name;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "department_id")
    private Department department;

}


StudentRepo.java

public interface StudentRepo extends JpaRepository<Student, Integer> {

}



StudentController.java

@RestController

@RequestMapping("/api/v1/students")

public class StudentController {

    @Autowired

    StudentRepo studentRepo;

    @Autowired

    StudentRepoCustom studentRepoCustom;





    @RequestMapping(value = "/", method = RequestMethod.GET)

    @Transactional(timeout = 10)

    public ResponseEntity<List<Student>> getAllStudents(){

        return new ResponseEntity<>(studentRepoCustom.getStudents(), HttpStatus.OK);

    }



    @RequestMapping(value = "/{studentId}", method = RequestMethod.GET)

    public ResponseEntity<?> getSingleStudent(@PathVariable int studentId){



        Optional<Student> s = studentRepo.findById(studentId);

        if(s.isPresent()){

            return new ResponseEntity<>(s, HttpStatus.OK);

        }else{

            Map<String, Object> errorMessage = new HashMap<>();

            errorMessage.put("message", "Entity Not Found");

            errorMessage.put("status", 404);

            return new ResponseEntity<>(errorMessage, HttpStatus.NOT_FOUND);

        }

    }

    

}




StudentService.java

@Service

public class StudentService implements StudentRepoCustom {

    @Autowired

    StudentRepo studentRepo;



    @Override

    public List<Student> getStudents() {

        try {

            TimeUnit.SECONDS.sleep(15);

        } catch (InterruptedException ie) {

            Thread.currentThread().interrupt();

        }

        return studentRepo.findAll();

    }

}


As you can see, StudentService class has a method getStudents() which returns all the student's records from the Student entity. For demonstration purposes, I have added an execution delay of 15 seconds(one may consider it a network delay). That means the method will take at least 15 seconds before it returns a response.

In StudentController, endpoint /students are annotated with @Transactional with a custom timeout of 10 seconds. This means the endpoint has to respond within 10 seconds before it throws an error. This endpoint is making a call to StudentService’s getStudents method that has a delay of 15 seconds.


Note: It is obvious, the endpoint /students will return a 500 error message due to the timeout limit as it requires a response within 10 seconds where as the student service will take at least 15 seconds to respond.

How to setup Request timeout in Spring Boot REST API? Example Tutorial



Error Response

org.springframework.transaction.TransactionTimedOutException: Transaction timed out: deadline was Sat Mar 04 19:26:04 GMT 2023 


{

"timestamp": "2023-03-04T14:26:09.851+00:00",

"status": 500,

"error": "Internal Server Error",

"path": "/api/v1/students/"

}


Successful Response

[ {

"id": 1,

"age": 26,

"name": "James Faulkner",

"department": {

"id": 1,

"name": "Chemistry",

"capacity": 110,

"specialization": "Micro Physics"

}

} ]


Using WebClient timeout

Spring introduces the WebFlux framework, which supports reactive programming. To make HTTP requests we use the WebClient interface. It is a reactive client that doesn't block when making HTTP requests. Its default underlying HTTP client library is Reactor Netty.

It allows us to set a timeout on a single external call rather than setting it on an entire endpoint. Although the majority of developers now use WebClient over RestTemplate, the older RestTemplate object from Spring may still have timeouts configured.

Let’s see WebClient in action with the help of an example.

Add WebFlux dependency in your project

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-webflux</artifactId>

    <version>3.0.4</version>

</dependency>


The example below shows how a WebClient request to a reactive downstream endpoint would typically appear.

webClient.get()

        .uri(uri)

        .retrieve()

        .onStatus(HttpStatus::isError, clientResponse -> {

            LOGGER.error("Error while calling endpoint {} with status code {}",

            uri.toString(), clientResponse.statusCode());

            throw new RuntimeException("Error while calling  accounts endpoint");

}).bodyToMono(JsonNode.class)

        .timeout(Duration.ofMillis(timeout));

Timeout

The time we wait after requesting a response is known as the response timeout. The responseTimeout() function may be used to set it up for the client.

Let's create a WebClient with a base URL that allows us to call oneself using localhost and a response timeout of 10 seconds

@Bean

public WebClient webClient() {

    return WebClient.builder()

            .baseUrl("http://localhost:8080/api/v1")

            .clientConnector(new ReactorClientHttpConnector(

                    HttpClient.create().responseTimeout(Duration.ofSeconds(10))

            ))

            .build();

}


Now that, our WebClient has been injected into our controller, we can use it to access our own /students endpoint, which still has a 15-sec timeout. Because we set the timeout for our WebClient to 10 seconds, it ought to crash considerably more quickly than 15 seconds.

@GetMapping("/webclient")

public Student[] getWithWebClient() {

    return webClient.get()

            .uri(uriBuilder -> uriBuilder

                    .path("/students/")

                    .build())

            .retrieve()

            .bodyToMono(Student[].class)

            .block();

}


We can see that after contacting this endpoint, we do indeed obtain a 500 HTTP error response indicating that the WebClient has timed out. The downstream @Transactional timeout can also be seen in the logs, but if we requested an external service rather than localhost, its timeout would be reported remotely.

With this method, it is possible to configure different request timeouts for various backend services, which may be required. Also, there are other techniques for managing errors in the Mono or Flux response that publishers get from WebClient in response to a general timeout issue.



Spring MVC timeout

There is another way to set a timeout in Spring Boot is by setting up the spring mvc property as mentioned below.
spring.mvc.async.request-timeout=milliseconds-precision


This allows us to define request timeout in milliseconds precision. It is to note here that, one can set this property externally, the property applies to the endpoints that return a Callable.

For demonstration, we have defined an endpoint that further calls the student’s service class and fetches the record.

@GetMapping("/mvc-timeout")

public Callable<List<Student>> getWithMvcRequestTimeout() {

    return () -> {

        return studentRepoCustom.getStudents();

    };

}


When we declare the application property, Spring immediately implements the configuration. When the timeout is reached, the response is promptly returned, and an explicit 503 HTTP error is given in place of a more general 500 error. This timeout option will be automatically inherited by all of the endpoints in our app.

Conclusion

That's all about the how to setup the request timeout in Spring Boot REST APIs and HTTP requests. With this, we have reached the end of our article. In this article, we have seen multiple ways to set request timeout in Spring Boot rest API. Among the methods, we have seen @Transactional timeout, Reactive programming WebClient, and Spring MVC.

One might wish to utilize Spring's @Transactional method and its timeout property to set a timeout on our database calls. 

The ideal way to create a global timeout for all requests is with the Spring MVC request-timeout property, however, WebClient also makes it simple to specify more specific timeouts for each resource. We are thankful for your valuable time. We hope this article has helped you understand the topic.

           
Other Java and Spring articles you may like
  • 10 Spring MVC annotations Java developers should learn (annotations)
  • 5 Spring Boot Annotations for full-stack Java developers (tutorial)
  • Top 5 Courses to learn Microservices in Java? (courses)
  • Top 5 Books and Courses to learn RESTful Web Service (books)
  • 10 Courses to learn Spring Security with OAuth 2 (courses)
  • 5 Courses to learn Spring Cloud and Microservices (courses)
  • Java + Spring Boot + Microservice exam (project)
  • 5 Spring Boot Features Every Java Developer Should Know (features)
  • 5 Course to Master Spring Boot online (courses)
  • 10 Things Java Developer should learn (goals)
  • 10 Tools Java Developers use in their day-to-day life (tools)
  • 3 ways to change Tomcat port in Spring Boot (tutorial)
  • 10 Tips to become a better Java developer (tips)
  • 5 courses to learn Spring Boot and Spring Cloud ( courses)
  • 10 Advanced Spring Boot Courses for Java Programmers (courses)
  • 20 Spring Boot Interview Questions for Java Programmers (questions)
  • 3 Best Practices Java Programmers can learn from Spring (best practices)

Thanks for reading this article so far. If you found this Java + Spring Boot + REST + HTTP Request timeout Tutorial useful and helpful then please share them with your colleagues and friends. If you have any questions or feedback then please drop a note. 

P. S. - If you are keen to learn about REST API and Microservice but new to this field and looking for free online courses then you can also checkout this list of the 5 best Microservice courses for beginners. In this post you will find free Microservice courses from Udemy, Coursera, and YouTube. 

No comments:

Post a Comment

Feel free to comment, ask questions if you have any doubt.