I have a @Service
with several methods, each method consumes a different web api. Each call should have a custom read timeout.
Is it thread-safe to have one RestTemplate instance and change the timeout via the factory in each method like so
((HttpComponentsClientHttpRequestFactory)restTemplate.getRequestFactory())
.setReadTimeout(customMillis);
My concern is that I'm changing the timeout on the factory and its not like a RequestConfig
. Will this approach be thread-safe considering these methods might get called by multiple users at the same time? Or each method should have its own RestTemplate
?
Option 1: More than one RestTemplate
If you are changing the properties of the connections created, you will need to have one RestTemplate
per configuration. I had this very same problem recently and had two versions of RestTemplate
, one for "short timeout" and one for "long timeout". Within each group (short/long) I was able to share that RestTemplate
.
Having your calls change the timeout settings, create a connection, and hope for the best is a race condition waiting to happen. I would play this safe and create more than one RestTemplate
.
Example:
@Configuration
public class RestTemplateConfigs {
@Bean("shortTimeoutRestTemplate")
public RestTemplate shortTimeoutRestTemplate() {
// Create template with short timeout, see docs.
}
@Bean("longTimeoutRestTemplate")
public RestTemplate longTimeoutRestTemplate() {
// Create template with short timeout, see docs.
}
}
And then you can wire them in to your services as needed:
@Service
public class MyService {
private final RestTemplate shortTimeout;
private final RestTemplate longTimeout;
@Autowired
public MyService(@Qualifier("shortTimeoutRestTemplate") RestTemplate shortTimeout,
@Qualifier("longTimeoutRestTemplate") RestTemplate longTimeout) {
this.shortTimeout = shortTimeout;
this.longTimeout = longTimeout;
}
// Your business methods here...
}
Option 2: Wrap calls in a Circuit Breaker
If you are calling out to external services, you probably should be using a circuit breaker for this. Spring Boot works well with Hystrix, a popular implementation of the circuit breaker pattern. Using hystrix you can control the fallback for each service you call out to, and the timeouts.
Suppose you have two options for Service A: 1) Cheap but sometimes slow 2) Expensive but fast. You can use Hystrix to give up on Cheap/Slow and use Expensive/Fast when you really need to. Or you can have no backup and just have Hystrix call a method that provides a sensible default.
Untested example:
@EnableCircuitBreaker
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp .class, args);
}
}
@Service
public class MyService {
private final RestTemplate restTemplate;
public BookService(RestTemplate rest) {
this.restTemplate = rest;
}
@HystrixCommand(
fallbackMethod = "fooMethodFallback",
commandProperties = {
@HystrixProperty(
name = "execution.isolation.thread.timeoutInMilliseconds",
value="5000"
)
}
)
public String fooMethod() {
// Your logic here.
restTemplate.exchange(...);
}
public String fooMethodFallback(Throwable t) {
log.error("Fallback happened", t);
return "Sensible Default Here!"
}
}
The fallback method has options too. You could annotate that method with @HystrixCommand
and attempt another service call. Or, you could just provide a sensible default.
Changing timeouts from the factory after RestTemplate
initialization is just a race condition waiting to occur (Like Todd explained). RestTemplate
was really designed to be built with pre-configured timeouts and for those timeouts to stay untouched after initialization. If you use Apache HttpClient
then yes you can set a RequestConfig
per request and that is the proper design in my opinion.
We are already using RestTemplate
everywhere in our project and we can't really afford the refactoring at the moment, that an http client switch would ensue.
For now I ended up with a RestTemplate
pooling solution, I created a class called RestTemplateManager and I gave it all responsibility of creating templates and pooling them. This manager have a local cache of templates grouped by service and readTimeout. Imagine a cache hashmap with the following structure:
ServiceA|1000 -> RestTemplate
ServiceA|3000 -> RestTemplate
ServiceB|1000 -> RestTemplate
The number in the key is the readTimeout in milliseconds (key can be adapted to support more than readTimeout later on). So when ServiceA requests a template with 1000ms read timeout, the manager will return the cached instance, if it doesn't exist it will be created and returned.
In this approach I saved myself from pre-defining RestTemplates, I only have to request a RestTemplate from the manager above. This also keeps initializations at a minimum.
This shall do until I have the time to ditch RestTemplate and use a more appropriate solution.
I assume you want read timeouts in case the response takes too long.
A possible solution would be to implement the timeout yourself by canceling the request if it hasn't completed in the given time.
To achieve this, you could use an AsyncRestTemplate
instead, which has builtin support for async operations like timeout and cancellation.
This gives you more control over the timeout for each request, example:
ListenableFuture<ResponseEntity<Potato>> future =
asyncRestTemplate.getForEntity(url, Potato.class);
ResponseEntity<Potato> response = future.get(5, TimeUnit.SECONDS);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With