package com.juick.config; import java.util.concurrent.TimeUnit; import org.apache.hc.client5.http.ConnectionKeepAliveStrategy; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.cookie.StandardCookieSpec; 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.HeaderElement; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.message.BasicHeaderElementIterator; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.Scheduled; /** * - Uses a connection pool to re-use connections and save overhead of creating connections. * - Has a custom connection keep-alive strategy (to apply a default keep-alive if one isn't specified) * - Starts an idle connection monitor to continuously clean up stale connections. */ @Configuration public class HttpClientConfig { private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientConfig.class); // Determines the timeout in milliseconds until a connection is established. private static final int CONNECT_TIMEOUT = 30000; // The timeout when requesting a connection from the connection manager. private static final int REQUEST_TIMEOUT = 30000; // The timeout for waiting for data private static final int SOCKET_TIMEOUT = 60000; private static final int MAX_TOTAL_CONNECTIONS = 50; private static final int DEFAULT_KEEP_ALIVE_TIME_MILLIS = 20 * 1000; private static final int CLOSE_IDLE_CONNECTION_WAIT_TIME_SECS = 30; @Bean PoolingHttpClientConnectionManager poolingConnectionManager() { PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager(); poolingConnectionManager.setMaxTotal(MAX_TOTAL_CONNECTIONS); return poolingConnectionManager; } @Bean ConnectionKeepAliveStrategy connectionKeepAliveStrategy() { return new ConnectionKeepAliveStrategy() { @Override public TimeValue getKeepAliveDuration(HttpResponse response, HttpContext context) { BasicHeaderElementIterator it = new BasicHeaderElementIterator (response.headerIterator("Keep-Alive")); while (it.hasNext()) { HeaderElement he = it.next(); String param = he.getName(); String value = he.getValue(); if (value != null && param.equalsIgnoreCase("timeout")) { return TimeValue.of(Long.parseLong(value) * 1000, TimeUnit.MILLISECONDS); } } return TimeValue.of(DEFAULT_KEEP_ALIVE_TIME_MILLIS, TimeUnit.MILLISECONDS); } }; } @Bean CloseableHttpClient httpClient() { RequestConfig requestConfig = RequestConfig.custom() .setCookieSpec(StandardCookieSpec.IGNORE) .setConnectionRequestTimeout(Timeout.of(REQUEST_TIMEOUT, TimeUnit.MILLISECONDS)) .setConnectTimeout(Timeout.of(CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)) .setResponseTimeout(Timeout.of(SOCKET_TIMEOUT, TimeUnit.MILLISECONDS)).build(); return HttpClients.custom() .setDefaultRequestConfig(requestConfig) .setConnectionManager(poolingConnectionManager()) .setKeepAliveStrategy(connectionKeepAliveStrategy()) .build(); } @Bean Runnable idleConnectionMonitor(final PoolingHttpClientConnectionManager connectionManager) { return new Runnable() { @Override @Scheduled(fixedDelay = 10000) public void run() { try { if (connectionManager != null) { LOGGER.trace("run IdleConnectionMonitor - Closing expired and idle connections..."); connectionManager.closeExpired(); connectionManager.closeIdle(TimeValue.of(CLOSE_IDLE_CONNECTION_WAIT_TIME_SECS, TimeUnit.SECONDS)); } else { LOGGER.trace("run IdleConnectionMonitor - Http Client Connection manager is not initialised"); } } catch (Exception e) { LOGGER.error("run IdleConnectionMonitor - Exception occurred. msg={}, e={}", e.getMessage(), e); } } }; } }