Skip to content

Commit

Permalink
feat : adv rest template configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
rajadilipkolli committed Mar 10, 2024
1 parent f1c9143 commit ed8267b
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 10 deletions.
13 changes: 7 additions & 6 deletions httpClients/boot-rest-template/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
Expand All @@ -66,6 +71,7 @@
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
Expand Down Expand Up @@ -99,11 +105,6 @@
<artifactId>spring-boot-testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
Expand Down Expand Up @@ -219,7 +220,7 @@
<configuration>
<java>
<googleJavaFormat>
<version>1.17.0</version>
<version>1.19.2</version>
<style>AOSP</style>
</googleJavaFormat>
</java>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,129 @@
package com.example.rest.template.config;

import static com.example.rest.template.utils.AppConstants.*;

import java.time.Duration;
import java.util.Iterator;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.ConnectionKeepAliveStrategy;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.web.client.RestTemplate;

@Configuration(proxyBeanMethods = false)
@EnableScheduling
@Slf4j
public class RestTemplateConfiguration {

@Bean
RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.build();
PoolingHttpClientConnectionManager poolingConnectionManager() {
PoolingHttpClientConnectionManager poolingConnectionManager =
new PoolingHttpClientConnectionManager();
// set a total amount of connections across all HTTP routes
poolingConnectionManager.setMaxTotal(MAX_TOTAL_CONNECTIONS);
// set a maximum amount of connections for each HTTP route in pool
poolingConnectionManager.setDefaultMaxPerRoute(MAX_ROUTE_CONNECTIONS);
// increase the amounts of connections if the host is localhost
HttpHost localhost = new HttpHost("http://localhost", 8080);
poolingConnectionManager.setMaxPerRoute(
new HttpRoute(localhost), MAX_LOCALHOST_CONNECTIONS);
return poolingConnectionManager;
}

@Bean
CloseableHttpClient httpClient(
PoolingHttpClientConnectionManager poolingConnectionManager,
ConnectionKeepAliveStrategy connectionKeepAliveStrategy) {
RequestConfig requestConfig =
RequestConfig.custom()
.setConnectTimeout(Timeout.ofSeconds(CONNECTION_TIMEOUT))
.setConnectionRequestTimeout(Timeout.ofSeconds(REQUEST_TIMEOUT))
.setResponseTimeout(Timeout.ofSeconds(SOCKET_TIMEOUT))
.build();

return HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(poolingConnectionManager)
.setKeepAliveStrategy(connectionKeepAliveStrategy)
.setConnectionManagerShared(true)
.build();
}

@Bean
ConnectionKeepAliveStrategy connectionKeepAliveStrategy() {
return (httpResponse, httpContext) -> {
Iterator<Header> headerIterator = httpResponse.headerIterator(HttpHeaders.KEEP_ALIVE);
while (headerIterator.hasNext()) {
Header element = headerIterator.next();
String param = element.getName();
String value = element.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
return TimeValue.ofSeconds(Long.parseLong(value));
}
}

return TimeValue.ofSeconds(DEFAULT_KEEP_ALIVE_TIME);
};
}

@Bean
RestTemplate restTemplate(
RestTemplateBuilder restTemplateBuilder, CloseableHttpClient httpClient) {

return restTemplateBuilder
.setConnectTimeout(Duration.ofSeconds(60))
.requestFactory(() -> new HttpComponentsClientHttpRequestFactory(httpClient))
.interceptors(
((request, body, execution) -> {
// log the http request
log.info("URI: {}", request.getURI());
log.info("HTTP Method: {}", request.getMethod().name());
log.info("HTTP Headers: {}", request.getHeaders());

return execution.execute(request, body);
}))
.build();
}

// close idleConnections
@Bean
Runnable idleConnectionMonitor(PoolingHttpClientConnectionManager pool) {
return new Runnable() {
@Override
@Scheduled(fixedDelay = 20000)
public void run() {
// only if connection pool is initialised
if (pool != null) {
log.info("cleaning connection pool");
pool.closeExpired();
pool.closeIdle(TimeValue.ofSeconds(IDLE_CONNECTION_WAIT_TIME));
}
}
};
}

// Required for Scheduler
@Bean
TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setThreadNamePrefix("idleMonitor");
scheduler.setPoolSize(5);
return scheduler;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.example.rest.template.utils;

import lombok.experimental.UtilityClass;

@UtilityClass
public final class AppConstants {
public static final String PROFILE_PROD = "prod";
public static final String PROFILE_NOT_PROD = "!" + PROFILE_PROD;
Expand All @@ -9,4 +12,22 @@ public final class AppConstants {
public static final String DEFAULT_PAGE_SIZE = "10";
public static final String DEFAULT_SORT_BY = "id";
public static final String DEFAULT_SORT_DIRECTION = "asc";

// Connection pool
public static final int MAX_ROUTE_CONNECTIONS = 40;
public static final int MAX_TOTAL_CONNECTIONS = 40;
public static final int MAX_LOCALHOST_CONNECTIONS = 80;

// Keep alive
public static final int DEFAULT_KEEP_ALIVE_TIME = 20; // 20 sec

// Timeouts
public static final int CONNECTION_TIMEOUT =
30; // 30 sec, the time for waiting until a connection is established
public static final int REQUEST_TIMEOUT =
30; // 30 sec, the time for waiting for a connection from connection pool
public static final int SOCKET_TIMEOUT = 60; // 60 sec, the time for waiting for data

// Idle connection monitor
public static final int IDLE_CONNECTION_WAIT_TIME = 30; // 30 sec
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ public class TestApplication {

@ServiceConnection
@Bean
PostgreSQLContainer<?> sqlContainer() {
return new PostgreSQLContainer<>("postgres:16.0-alpine");
PostgreSQLContainer<?> postgreSQLContainer() {
return new PostgreSQLContainer<>("postgres:16.2-alpine");
}

public static void main(String[] args) {
Expand Down

0 comments on commit ed8267b

Please sign in to comment.