Socket timeout not working in Rest template third party API call - Spring boot - spring-boot

I am trying to test response-time out by configuring socket time out when third party rest service call. I am calling external web service by Spring Rest Template in my service.
For response timeout testing purpose, the external web service is taking more time which I configured.
I have configured 1600 milliseconds for timeout, but unfortunately I am getting response in more then configured time, around 2500 - 3000 milliseconds.
As per the configuration I should get time out exception.
public ClientHttpRequestFactory getClientHttpRequestFactory(String timeout) {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(Integer.parseInt(timeout))
.setConnectionRequestTimeout(Integer.parseInt(timeout))
.setSocketTimeout(Integer.parseInt(timeout))
.build();
CloseableHttpClient closeableHttpClient = HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig)
.build();
return new HttpComponentsClientHttpRequestFactory(closeableHttpClient);
}
public String milisecTimeout = "1600";
RestTemplate restTemplate = new RestTemplate(appConfig.getClientHttpRequestFactory(milisecTimeout));
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("Content-Type", "application/json");
httpHeaders.set("Accept", "application/json");
httpHeaders.set("Accept-Charset", "UTF-8");
HttpEntity<String> httpEntity = new HttpEntity<>(request, httpHeaders);
String responseBody = "";
try {
ResponseEntity<String> response = restTemplate.exchange(hostUrl, HttpMethod.POST, httpEntity, String.class);
String statusCode = response.getStatusCodeValue();
String responseBody = response.getBody();
SearchRS searchSdnRS = objectMapper.readValue(responseBody, SearchRS.class);
} catch (Exception ex){
log.error("Error:", ex.getCause());
}
Please correct me if any misunderstanding.

Socket timeout is defined as maximum time of inactivity between two data packets. It's not about total request duration. So in the case you're describing it could well be that the data transfer from server to client started after 1500 milliseconds and lasted 1000–1500 milliseconds.

Related

Spring restTemplate timeout without throwing exceptioin

I found a timeout issue on my backend service: SA.
SA send request to 3party api with restTemplate:
try {
log.info("sending request to sb"); //position 1
return restTemplate.postForObject("https://url.to.sb.com", null, String.class);
}catch(Exception ex) {
log.error("request error", ex) //position 2
}
The sb service is working normally.
Browser get gateway timeout error .
Logs on position 1 is printed but position 2 is not printed.
Here is how I compose the restTemplate:
RestTemplate restTemplate = new RestTemplate();
HttpClientBuilder httpClientBuilder = HttpClients.custom();
httpClientBuilder.setSSLContext(xxxx);
CloseableHttpClient client = httpClientBuilder.build();
restTemplate.setInterceptors(Arrays.asList(new DefaultHttpRequestInterceptor()));
HttpComponentsClientHttpRequestFactory requestFactory = new
HttpComponentsClientHttpRequestFactory(client);
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(requestFactory));
return restTemplate;
Why frontend get gateway timeout, but there isn't error log on SA ?
Seems that thread is pending. And this issue is resolved after restart SA .
But I still want to know the possible causes of this issue,
not sure it's related with requestFactory setTimeout

why my spring webflux vs spring mvc load test throughput is same?

We are comparing the throughput of the spring webflux vs spring mvc for one of our project.
We have service1 and service2. Service1 prepares content and posts it to service2 using webclient. The code below is from service1 which posts content to service2.
Note: Service 2 takes 500ms to process a single request.
Spring Webflux
#PutMapping(path = "/")
public Mono<String> create() throws Exception {
ClassPathResource classpathResource = new ClassPathResource("/file.zip");
MultipartBodyBuilder multipartBodyBuilder = new MultipartBodyBuilder();
multipartBodyBuilder.part("key", "value");
multipartBodyBuilder.part("file", classpathResource, MediaType.APPLICATION_OCTET_STREAM);
MultiValueMap<String, HttpEntity<?>> multiValueMap = multipartBodyBuilder.build();
return WebClient.create()
.post()
.uri(uri)
.contentType(MediaType.MULTIPART_FORM_DATA)
.headers(httpHeaders -> httpHeaders.setBearerAuth(token))
.body(BodyInserters.fromMultipartData(multiValueMap))
.retrieve()
.bodyToMono(String.class);
}
Spring MVC
#PutMapping(path = "/")
public ResponseEntity<String> create() throws Exception {
String response = null;
ClassPathResource classpathResource = new ClassPathResource("/file.zip");
try (InputStream zipInputStream = new BufferedInputStream(classpathResource.getInputStream())) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.setBearerAuth(token);
LinkedMultiValueMap<String, String> fileMap = new LinkedMultiValueMap<>();
ContentDisposition contentDisposition = ContentDisposition.builder("form-data").name("file").filename("file")
.build();
fileMap.add(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString());
fileMap.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);
HttpEntity<InputStreamResource> sipEntity = new HttpEntity<InputStreamResource>(
new InputStreamResource(zipInputStream), fileMap);
LinkedMultiValueMap<String, String> formatfileMap = new LinkedMultiValueMap<>();
contentDisposition = ContentDisposition.builder("form-data").name("key")
.build();
formatfileMap.add(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString());
HttpEntity<String> formatEntity = new HttpEntity<String>("value", formatfileMap);
LinkedMultiValueMap<String, Object> linkedMultiValueMap = new LinkedMultiValueMap<String, Object>();
linkedMultiValueMap.add("file", sipEntity);
linkedMultiValueMap.add("key", formatEntity);
HttpEntity<LinkedMultiValueMap<String, Object>> request = new HttpEntity<LinkedMultiValueMap<String, Object>>(
linkedMultiValueMap, headers);
response = restTemplate.postForObject(ingestUrl, request, String.class);
}
return new ResponseEntity<String>(response, HttpStatus.OK);
}
We did a load test with 200, 300, 500, 1000 concurrent users using jmeter. In all the cases we have received same throughput for both frameworks. Are we doing something wrong here?
I have captured load test stats using Gatling for 1000 users for both mvc and reactive.
Webflux - 1000 users
Spring webflux with 1000 concurrent users
MVC - 1000 users
Spring MVC with 1000 concurrent users
I wouldn't say you did something wrong, rather this specific use case is not suitable for a hard comparison. Both endpoints rely on first loading something from a disk and then making an HTTP call.
The limiting factor here (I guess) is for both the network call, which takes the same time to finish nonetheless you use WebFlux or the MVC.
The benefit you get from WebFlux is the non-blocking behaviour, which scales better with fewer resources under load.
Something similar was already answered quite well on Quora: https://www.quora.com/Does-Spring-WebFlux-perform-better-than-Spring-MVC

413 request entity too large issue in HTTP Get method call

Please don't mistake it as already asked question. The main difference here is, this issue is coming in Get method call. All the solutions discussed here or other places talks about either POST method or multipart form data.
Configuration I have provided is as below:
String url = env.getProperty(ApplicationConstants.PMCC_MANAGER_REGION_QUERY_URL);
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(url).queryParam("inTIS_PMA_NUMBER","ALL");
URI uri = builder.build().toUri();
RestTemplate restTemplate = getRestTemplateBuilder().build();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class);
private RestTemplateBuilder getRestTemplateBuilder() {
Integer connectionTimeOut = 2000;
Integer readTimeOut = 3000;
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
restTemplateBuilder.setConnectTimeout(connectionTimeOut);
restTemplateBuilder.setReadTimeout(readTimeOut);
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setBufferRequestBody(false);
restTemplateBuilder.requestFactory(factory);
return restTemplateBuilder;
}
This code is part of Spring Boot application and deployed in WebLogic server.

RestTemplate call returns 401 Unauthorized

Background
I am trying to consume a REST endpoint hosted on IBM Cloud API from my SpringBoot application using RestTemplate. I am using the following snippet to make the call:
RestTemplate send = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setCacheControl(CacheControl.noCache());
headers.set("x-ibm-client-id", clientId);
headers.set("x-ibm-client-secret", clientSecret);
HttpEntity<BodyEntity> httpEntity = new HttpEntity<>(bodyEntity, headers);
send.exchange(ENDPOINT_URL, HttpMethod.POST, httpEntity, Object.class);
I used the following snippet to configure RestTemplate
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
Problem
Using this snippet, when the call is made I receive 401 Unauthorized. When I made the same call using Postman, I received correct response from server without any problem.
Since I received 401 response code I set to further investigate the request by logging headers and body and other parts of request.
I implemented ClientHttpRequestInterceptor to log outgoing requests to further debug the issue and added this interceptor to my RestTemplate config as follows:
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
// new code
builder.interceptors(new LoggingClientHttpRequestInterceptor());
return builder.build();
}
After making the request again, I could see in the log that the outgoing call contained all details as it should e.g. Headers and Body were correct.
After this, I changed the whole thing to use Apache HTTP Client as follows:
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(URL);
String reqString = "BODY";
httpPost.setEntity(new StringEntity(reqString, ContentType.APPLICATION_JSON));
httpPost.setHeader("accept", "application/json");
httpPost.setHeader("content-type", "application/json");
httpPost.setHeader("cache-control", "no-cache");
httpPost.setHeader("x-ibm-client-id", clientId);
httpPost.setHeader("x-ibm-client-secret", clientSecret);
CloseableHttpResponse response = httpClient.execute(httpPost);
try {
System.out.println("Response status: " + response.getStatusLine());
HttpEntity entity1 = response.getEntity();
System.out.println("Response :" + entity1.toString());
} finally {
response.close();
}
Using the snippet above, I executed the request and received correct response.
Question
Why RestTemplate call returns and error whereas HttpClient returns correct response?
Do I need to further configure RestTemplate?
What have I missed?

OAuth2RestTemplate TCP connections

I'm using a Spring OAuth2RestTemplate with ClientCredentialsResourceDetails to acquire an API authorization token. The authorization server and the API endpoints are hidden behind the same load balancers (LB). We have an issues where the first connection to the API endpoint, after acquiring the token, fails with a 404 error message but subsequent calls to the same API endpoint with the same token are successful. I believe the LB is miss-configured in some way but we've been asked if we could try using separate TCP sessions for the acquisition of the token and then the REST call. Is there a way to get the Spring RestTemplate to do this?
UPDATE
Here's how I create and configure the template:
#Bean
public OAuth2RestTemplate oauth2RestTemplate(
#Value("${token.uri}") final String tokenUri,
#Value("${token.clientId:client}") final String clientId,
#Value("${token.secret:secret}") final String clientSecret,
#Value("${token.scope:platform}") final String scope,
final MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter)
{
ClientCredentialsResourceDetails rd = new
ClientCredentialsResourceDetails();
rd.setAuthenticationScheme(AuthenticationScheme.header);
rd.setAccessTokenUri(tokenUri);
rd.setClientId(clientId);
rd.setClientSecret(clientSecret);
rd.setScope(Arrays.asList(scope));
OAuth2RestTemplate rt = new OAuth2RestTemplate(rd);
List<HttpMessageConverter<?>> converters = rt.getMessageConverters();
converters.add(customJackson2HttpMessageConverter);
rt.setMessageConverters(converters);
return rt;
}
and here's the call to the api:
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.set("Connection", "close"); // hmm, gets replace by keep-alive on the token api request!
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<MyObject[]> response = restTemplate.exchange(
"http://example.com/api/v1/rest/method",
HttpMethod.GET, entity, MyObject[].class);
Thanks.
Try adding the Connection request header with value as close while sending your request using resttemplate. This should force the TCP connection to be closed after each request. Not very performant though.
HttpHeaders headers = new HttpHeaders();
headers.set("Connection", "close");
This is only for the "but we've been asked if we could try using separate TCP sessions for the acquisition of the token and then the REST call." part of your question. It will not help resolve your 404 (that does seem to be an LB issue).
UPDATE: Since you're using OAuth2RestTemplate, create a ClientHttpRequestInterceptor which injects the header.
public class ConnectionCloseInterceptor implements ClientHttpRequestInterceptor {
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpHeaders headers = request.getHeaders();
headers.add("Connection", "close");
return execution.execute(request, body);
}
}
Use it in your rest template (OAuth2RestTemplate extends RestTemplate so below applies to both) like so (when you create the rest template bean):
List<ClientHttpRequestInterceptor> currentInterceptors = new ArrayList<>(restTemplate.getInterceptors()); //Don't want to lose the other interceptors!
currentInterceptors.add(new ConnectionCloseInterceptor()); //Add ours
restTemplate.setInterceptors(currentInterceptors);

Resources