Retry java RestTemplate HTTP request if host offline - java

Hi I'm using the spring RestTemplate for calling a REST API. The API can be very slow or even offline. My application is building the cache by sending thousands of requests one after the other. The responses can be very slow too, because they contains a lot of data.
I have already increased the Timeout to 120 seconds. My problem now it that the API can be offline and I get a org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool exception.
In the case when the API ist offline, the application should wait and try again until the API is online again.
Can I achieve this in RestTemplate out of the box without building exception-loops on my own?
Thanks!

I had same situation and done some googling found the solution. Giving answer in hope it help someone else. You can set max try and time interval for each try.
#Bean
public RetryTemplate retryTemplate() {
int maxAttempt = Integer.parseInt(env.getProperty("maxAttempt"));
int retryTimeInterval = Integer.parseInt(env.getProperty("retryTimeInterval"));
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(maxAttempt);
FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
backOffPolicy.setBackOffPeriod(retryTimeInterval); // 1.5 seconds
RetryTemplate template = new RetryTemplate();
template.setRetryPolicy(retryPolicy);
template.setBackOffPolicy(backOffPolicy);
return template;
}
And my rest service that i want to execute is below.
retryTemplate.execute(context -> {
System.out.println("inside retry method");
ResponseEntity<?> requestData = RestTemplateProvider.getInstance().postAsNewRequest(bundle, ServiceResponse.class, serivceURL,
CommonUtils.getHeader("APP_Name"));
_LOGGER.info("Response ..."+ requestData);
throw new IllegalStateException("Something went wrong");
});

You can also tackle this annotation-driven using Spring Retry. This way you will avoid to implement the template.
Add it to your pom.xml
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.1.2.RELEASE</version>
</dependency>
Enable it for your application/configuration
#SpringBootApplication
#EnableRetry
public class MyApplication {
//...
}
Guard methods that are in danger of failure with #Retryable
#Service
public class MyService {
#Retryable(maxAttempts=5, value = RuntimeException.class,
backoff = #Backoff(delay = 15000, multiplier = 2))
public List<String> doDangerousOperationWithExternalResource() {
// ...
}
}

Use Spring Retry project (https://dzone.com/articles/spring-retry-ways-integrate, https://github.com/spring-projects/spring-retry).
It is designed to solve problems like yours.

This approach saved my day.!!!Resilience saved my day.[Resilience retry][1]
RetryConfig config = RetryConfig.custom()
.maxAttempts(4)
.waitDuration(Duration.ofMillis(2000))
.failAfterMaxAttempts(true)
.build();
RetryRegistry registry = RetryRegistry.of(config);
HttpEntity<String> request =
new HttpEntity<>(body, headers);
Retry retry = registry.retry("notification-endpoint-"+System.currentTimeMillis());
AtomicReference<Integer> retries = new AtomicReference<>(0);
retry.getEventPublisher().onRetry(e -> {
log.info("Retrying here!!!. Count: {}", retries.updateAndGet(v -> v + 1));
}).onError(e->{
log.error("Failed to get to client.");
});
if(requestPojo.getMethod().equalsIgnoreCase("GET")) {
response = Retry.decorateCheckedSupplier(
retry,
() -> restTemplateConfig.restTemplate()
.exchange(url, HttpMethod.GET, request, String.class)).unchecked().get();
}
else if(requestPojo.getMethod().equalsIgnoreCase("POST")) {
response = Retry.decorateCheckedSupplier(
retry,
() -> restTemplateConfig.restTemplate()
.exchange(url, HttpMethod.POST, request, String.class)).unchecked().get();
}```
[1]: https://resilience4j.readme.io/docs/retry

Related

Spring boot mock method response

I need some help with unit tests. Our company has decided to start using TDD, and I'm supposed to implement that, but I've got very limited experience with unit tests in general, so I'm trying to cover some of the old code to get up to speed. That's when I got stuck with this:
public Analytics generateAnalytics(String id, String domain) {
List<Result> totalResults = new ArrayList<>();
for(String url : ANALYTIC_URLS) {
url += domain;
String scrubbedUrl = url.replace(API_KEY, "XXXXXXXXXX");
Audit audit = new Audit(id, scrubbedUrl);
try {
totalResults.add(new Result(getData(url, audit)));
} catch(Exception e) {
audit.setResponse(e.toString());
throw new Exception(e);
} finally {
auditRepository.save(audit);
}
}
return composeAnalytics(totalResults);
}
private List<Map<String, String>> getData(String request, Audit audit) throws Exception {
try (CloseableHttpClient client = HttpClients.createDefault()) {
CloseableHttpResponse response = client.execute(new HttpGet(request));
if(response.getStatusLine().getStatusCode() == 200) {
return readInputStream(response.getEntity().getContent(), audit);
} else {
throw new Exception(response.toString());
}
}
}
My issue is, when I wanna test the generateAnalytics method, the getData method goes and gets data from a live API that costs units per each request. Obviously I wanna stop this from bleeding out all our units during the testing. I've tried mocking the ClosableHttpClient like so:
#Mock
CloseableHttpClient client;
#InjectMocks
Service service;
#Test
void testTest() throws Exception {
when(client.execute(any())).thenReturn(mock(CloseableHttpResponse.class));
service.generateAnalytics("123", "no.com");
assertEquals(true, true);
}
This works when there's another service that needs to be mocked in one of my other tests, but in this case it still calls the API and drains our units. What should I do about this?
That's because your client mock is never used:
try (CloseableHttpClient client = HttpClients.createDefault()) {
Either mock HttpClients.createDefault() or , maybe better, inject the client into the service instead of creating on the fly.
Have you try to use MockMvc?
With MockMvc you can perform requests against a mocked servlet environment.
There won't be any real HTTP communication for such tests.
Official documentation: spring.io/testing-web

Programmatically call spring cloud config actuator/env post endpoint in spring boot application

I am using spring cloud config in my spring boot application, I am trying to update the property value through actuator/env post endpoint.
Here is my code:
#Service
public class ActuatorRefreshService {
private final String inventoryModeKey = "inventory.initial.mode";
private WebClient webClient = WebClient.builder().build();
#Autowired
public ActuatorRefreshService() {
}
public void refreshStatus(String latestMode) {
Map<String, String> bodyMap = new HashMap();
bodyMap.put("name",inventoryModeKey);
bodyMap.put("value",latestMode);
webClient.post().uri("/actuator/env")
.header(HttpHeaders.CONTENT_TYPE, String.valueOf(MediaType.APPLICATION_JSON))
.body(Mono.just(bodyMap), Map.class).retrieve().onStatus(HttpStatus::isError, clientResponse -> {
return Mono.error(new Exception("error"));
}).bodyToMono(String.class);
System.out.println("call actuator endpoint to update the value");
}
}
When I am calling my rest endpoint that calls this refreshStatus method. The api returns me 200 status. After that I hit localhost:8080/actuator/refresh. When I check the updated value , it shows this __refreshAll__.
I have no idea why is this happening?? Any help would be appreciated.
Note :* When I hit the endpoint localhost:8080/actuator/env from postman and refresh then it updates the property.*
I tried with localhost:8080/actuator/busenv bus endpoint as well but still no luck.
Anyone tried this kind of requirement?
I was able to call this api localhost:8080/actuator/busenv programmatically in spring boot using RestTemplate in spring. It is refreshing the configuration as well in application context. I am not sure why it didn't work with WebClient though.
posting the answer if anyone else is looking for the same requirement.
Please find the below code
#Service
public class ActuatorRefreshService {
private final String inventoryModeKey = "inventory.initial.mode";
#Autowired
public ActuatorRefreshService() {
}
public void refreshInventoryMode(String latestMode) {
Map<String, String> bodyMap = new HashMap();
bodyMap.put("name",inventoryModeKey);
bodyMap.put("value",latestMode);
final String url = "http://localhost:8080/actuator/busenv";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers
.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
HttpEntity<Object> entity = new HttpEntity<>(bodyMap, headers);
restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
System.out.println("call actuator endpoint to update the value ");
}
}

We are using microservices . How to increase timeout of a particular springboot api?

I want to increase the timeout of an API at the controller level. For all API we can do by mentioning the following in my yml file:
ribbon:
ReadTimeout: 30000
ConnectTimeout: 30000
But I want timeout increase timeout for a particular API. As it is a long process API. How can we achieve this?
#GetMapping(value = { "", "/" })
public ResponseEntity<Page<DBInventoryMasterEntity>> fetch() {
Page<DBInventoryMasterEntity> returnList = null;
returnList = inventoryService.findByCustomerCode();
return ResponseEntity.ok(returnList);
}
You can try these two methods:
Return a Callable<>. See this answer.
Use #Transactional annotation which takes a timeout (in seconds) parameter
#GetMapping(value = { "", "/" })
#Timed
#Transactional(timeout = 120) // 2 minutes
public ResponseEntity<Page<DBInventoryMasterEntity>> fetch() {
// your code
}

Spring RestTemplate - async vs sync restTemplate

I wrote the following code to test the performance of both the sync RestTemplate and AsyncRestTemplate. I just ran it a few times manually on POSTMAN.
We are just passing 10 references into a GET call so that we can return 10 links:
RestTemplate - synchronous and returns in 2806ms:
ArrayList<String> references = new ArrayList<>();
ArrayList<String> links = new ArrayList<>();
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
for (int i = 0; i < 10; i++) {
ResponseEntity<String> resource = restTemplate.getForEntity(references.get(i), String.class);
links.add(resource.getBody().toString());
}
RestTemplate - asynchronous and returns in 2794ms:
//Creating a synchronizedList so that when the async resttemplate returns, there will be no concurrency issues
List<String> links = Collections.synchronizedList(new ArrayList<String>());
//CustomClientHttpRequestFactory just extends SimpleClientHttpRequestFactory but disables automatic redirects in SimpleClientHttpRequestFactory
CustomClientHttpRequestFactory customClientHttpRequestFactory = new CustomClientHttpRequestFactory();
//Setting the ThreadPoolTaskExecutor for the Async calls
org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor pool = new org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor();
pool.setCorePoolSize(5);
pool.setMaxPoolSize(10);
pool.setWaitForTasksToCompleteOnShutdown(true);
pool.initialize();
//Setting the TaskExecutor to the ThreadPoolTaskExecutor
customClientHttpRequestFactory.setTaskExecutor(pool);
ArrayList<String> references = new ArrayList<>();
ArrayList<String> links = new ArrayList<>();
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate(customClientHttpRequestFactory);
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
for (int i = 0; i < 10; i++) {
Future<ResponseEntity<String>> resource = asyncRestTemplate.getForEntity(references.get(i), String.class);
ResponseEntity<String> entity = resource.get(); //this should start up 10 threads to get the links asynchronously
links.add(entity.getBody().toString());
}
In most cases, both methods actually return back the results with a very similar time, averaging 2800ms in both async and sync calls.
Am I doing something incorrect as I would have expected the async call to be much faster?
Nowadays, AsyncRestTemplate is #Deprecated in favor of WebClient. So nobody should use that class anymore!
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.html
I would say that you're missing the real benefits of the AsyncRest here.
You should add callbacks to each requests you're sending so that the response will be processes only when available.
Indeed, the getForEntity method of an AsyncRestTemplate returns a ListenableFuture to which you can connect a callback task. See the official doc ListenableFuture for further information.
For example in your case it could be:
for (int i = 0; i < 10; i++) {
ListenableFuture<ResponseEntity<String>> response = asyncRestTemplate.getForEntity(references.get(i), String.class);
response.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
#Override
public void onSuccess(ResponseEntity<String> result) {
// Do stuff onSuccess
links.add(result.getBody().toString());
}
#Override
public void onFailure(Throwable ex) {
log.warn("Error detected while submitting a REST request. Exception was {}", ex.getMessage());
}
});
}
The tricky thing with Java Future is that it's not composable and it's really easy to block.
In this case, calling future.get() makes your code block and wait until the response is back. In fact, this approach makes sequential calls and does not leverage the async nature of this RestTemplate implementation.
The simplest way to fix this is to separate it in two loops:
ArrayList<Future<ResponseEntity<String>>> futures = new ArrayList<>();
for (String url : references.get()) {
futures.add(asyncRestTemplate.getForEntity(url, String.class)); //start up to 10 requests in parallel, depending on your pool
}
for (Future<ResponseEntity<String>> future : futures) {
ResponseEntity<String> entity = future.get(); // blocking on the first request
links.add(entity.getBody().toString());
}
Obviously there are more elegant solutions, especially if using JDK8 streams, lambdas and ListenableFuture/CompletableFuture or composition libraries.

How to use RestTemplate efficiently with RequestFactory?

I am working on a project in which I need to make a HTTP URL call to my server which is running Restful Service which returns back the response as a JSON String. I am using RestTemplate here along with HttpComponentsClientHttpRequestFactory to execute an url.
I have setup a http request timeout (READ and CONNECTION time out) on my RestTemplate by using HttpComponentsClientHttpRequestFactory.
Below is my Interface:
public interface Client {
// for synchronous
public String getSyncData(String key, long timeout);
// for asynchronous
public String getAsyncData(String key, long timeout);
}
Below is my implementation of Client interface -
public class DataClient implements Client {
private final RestTemplate restTemplate = new RestTemplate();
private ExecutorService executor = Executors.newFixedThreadPool(10);
// for synchronous call
#Override
public String getSyncData(String key, long timeout) {
String response = null;
try {
Task task = new Task(key, restTemplate, timeout);
// direct call, implementing sync call as async + waiting is bad idea.
// It is meaningless and consumes one thread from the thread pool per a call.
response = task.call();
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
}
return response;
}
// for asynchronous call
#Override
public Future<String> getAsyncData(String key, long timeout) {
Future<String> future = null;
try {
Task task = new Task(key, restTemplate, timeout);
future = executor.submit(task);
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
}
return future;
}
}
And below is my simple Task class
class Task implements Callable<String> {
private RestTemplate restTemplate;
private String key;
private long timeout; // in milliseconds
public Task(String key, RestTemplate restTemplate, long timeout) {
this.key = key;
this.restTemplate = restTemplate;
this.timeout = timeout;
}
public String call() throws Exception {
String url = "some_url_created_by_using_key";
// does this looks right the way I am setting request factory?
// or is there any other effficient way to do this?
restTemplate.setRequestFactory(clientHttpRequestFactory());
String response = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
return response;
}
private static ClientHttpRequestFactory clientHttpRequestFactory() {
// is it ok to create a new instance of HttpComponentsClientHttpRequestFactory everytime?
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(timeout); // setting timeout as read timeout
factory.setConnectTimeout(timeout); // setting timeout as connect timeout
return factory;
}
}
Now my question is - Does the way I am using RestTemplate along with setRequestFactory in the call method of Task class everytime is efficient? Since RestTemplate is very heavy to be created so not sure whether I got it right.
And is it ok to create a new instance of HttpComponentsClientHttpRequestFactory everytime? Will it be expensive?
What is the right and efficient way to use RestTemplate if we need to setup Read and Connection timeout on it.
This library will be used like this -
String response = DataClientFactory.getInstance().getSyncData(keyData, 100);
From what I can tell, you're reusing the same RestTemplate object repeatedly, but each Task is performing this line: restTemplate.setRequestFactory(clientHttpRequestFactory());. This seems like it can have race conditions, e.g. one Task can set the RequestFactory that another Task will then accidentally use.
Otherwise, it seems like you're using RestTemplate correctly.
How often do your timeouts change? If you mostly use one or two timeouts, you can create one or two RestTemplates using the RequestFactory constructor with the pre-loaded timeout. If you're a stickler for efficiency, create a HashMap<Integer, RestTemplate> that caches a RestTemplate with a particular timeout each time a new timeout is requested.
Otherwise, looking at the code for RestTemplate's constructor, and for HttpComponentsClientHttpRequestFactory's constructor, they don't look exceptionally heavy, so calling them repeatedly probably won't be much of a bottleneck.

Categories

Resources