Reputation: 482
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
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
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
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
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
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