Tom Shir
Tom Shir

Reputation: 482

How to set a timeout on a Spring Boot REST API?

I have some REST APIs that may take a while to execute, and I want to limit their execution duration. Preferably, if 30 seconds passed and the request didn't return, I would like to return a specific HTTP code / data and terminate that request completly.

The current code:

@RestController
@CrossOrigin(origins = {"*"}, maxAge = 4800, allowCredentials = "false")
public class APIController {

@RequestMapping(value = "/api/myapifunc", method = RequestMethod.POST, produces = "application/json")
public ResponseEntity<?> optimize(@RequestParam(value="param1", defaultValue="")) {
    // Code here
}

Upvotes: 16

Views: 30869

Answers (5)

alan9uo
alan9uo

Reputation: 1131

You can use Future timeout:

final Future<Object> submit = service.submit(new Callable<Object>() {
    @Override
    public Object call() throws Exception {
        ......YOUR CODE
        return "";
    }
});
try {
    submit.get(3, TimeUnit.SECONDS);
} catch (Exception e) {
    log.error("fail",e);
}

Upvotes: -1

Graham Lea
Graham Lea

Reputation: 6343

With Spring Boot 2.3 / Tomcat 9, you can set a timeout for ALL incoming HTTP requests to complete by installing a Tomcat StuckThreadDetectionValve. Here's the Spring configuration code you'll need (it's Kotlin):

import org.apache.catalina.valves.StuckThreadDetectionValve
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class RequestTimeoutConfiguration(
    @Value("\${app.tomcat.stuck-thread-detection.request-timeout.seconds}")
    private val stuckThreadTimeoutSeconds: Int
) {

    @Bean
    fun stuckThreadDetectionValve() =
        StuckThreadDetectionValve().apply {
            threshold = stuckThreadTimeoutSeconds
            interruptThreadThreshold = stuckThreadTimeoutSeconds
        }

    @Bean
    fun stuckThreadDetectionWebServerFactoryCustomizer(valve: StuckThreadDetectionValve) =
        WebServerFactoryCustomizer { factory: TomcatServletWebServerFactory ->
            factory.addContextValves(valve)
        }
}

Then you just need the property in application.properties to control it:

app.tomcat.stuck-thread-detection.request-timeout.seconds=130

Upvotes: 4

Jonathan JOhx
Jonathan JOhx

Reputation: 5978

You can set this property configuration

 server.connection-timeout=30000 

in your application.properties. Based on official documentation says:

server.connection-timeout= # Time that connectors wait for another HTTP request before closing the connection. When not set, the connector's container-specific default is used. Use a value of -1 to indicate no (that is, an infinite) timeout.

Upvotes: -3

Karol Dowbecki
Karol Dowbecki

Reputation: 44980

It looks like you are describing the Circuit Breaker pattern. If you have control over both the client and server code and want to explore Spring Cloud and Netflix Hysterix libraries you can take a look at Getting Started: Circuit Breaker guide.

If you are using Apache Tomcat as your servlet container you can configure Stuck Thread Detection Valve:

This valve allows to detect requests that take a long time to process, which might indicate that the thread that is processing it is stuck. Additionally it can optionally interrupt such threads to try and unblock them.

When such a request is detected, the current stack trace of its thread is written to Tomcat log with a WARN level.

The IDs and names of the stuck threads are available through JMX in the stuckThreadIds and stuckThreadNames attributes. The IDs can be used with the standard Threading JVM MBean (java.lang:type=Threading) to retrieve other information about each stuck thread.

Upvotes: 5

SKD
SKD

Reputation: 87

@RequestMapping(value = "/api/myapifunc", method = RequestMethod.POST, produces = 
"application/json")
public ResponseEntity<?> optimize(@RequestParam(value="param1", defaultValue="")) {
 return new Callable<String>() {
    @Override
    public String call() throws Exception {
        Thread.sleep(3000); //this will cause a timeout
        return "foobar";
    }
  };
}

Future you can use or annotation @Timed @Transactional(timeout = 3000)

Upvotes: 0

Related Questions