diff --git a/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithConfig.java b/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithConfig.java index 2183554..ed46fe1 100644 --- a/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithConfig.java +++ b/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithConfig.java @@ -41,6 +41,7 @@ import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.apache.hc.core5.net.URIBuilder; import org.apache.hc.core5.ssl.SSLContextBuilder; +import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.springframework.security.web.util.UrlUtils; @@ -155,6 +156,9 @@ public class HttpClientAccessTokenServiceWithConfig implements HttpClientAccessT private static final long DEFAULT_EXPIRES_IN = 3600L; + /** 캐시한 HttpClient 의 유휴 커넥션 정리 주기(초) */ + private static final int IDLE_CONNECTION_EVICT_SECONDS = 30; + /** * mTLS 여부/clientId 조합별로 CloseableHttpClient(및 내부 PoolingHttpClientConnectionManager)를 * 1회만 생성해 재사용한다. 이 서비스 인스턴스는 HttpClientAccessTokenServiceFactoryByDB 에 @@ -381,8 +385,11 @@ public class HttpClientAccessTokenServiceWithConfig implements HttpClientAccessT requestConfigBuilder.setProxy(proxy); } + // 커넥션 풀 대기 시간을 지정하지 않으면 기본 3분을 기다린다. 스케줄러의 태스크 타임아웃(30초)이 + // 먼저 걸려 요청이 인터럽트되고, 그 과정에서 커넥션이 반납되지 않아 풀이 마르는 악순환이 생긴다. RequestConfig requestConfig = requestConfigBuilder .setConnectTimeout(Timeout.ofMilliseconds(connectionTimeout)) + .setConnectionRequestTimeout(Timeout.ofMilliseconds(connectionTimeout)) .setResponseTimeout(Timeout.ofMilliseconds(timeout)).build(); request.setConfig(requestConfig); @@ -641,7 +648,10 @@ public class HttpClientAccessTokenServiceWithConfig implements HttpClientAccessT adapterGroupName, useMtls, clientId, contentCompression, defaultUserAgent); } - HttpClientBuilder builder = HttpClients.custom().setConnectionManager(connectionManager); + // HttpClient 를 캐시해 재사용하므로 죽은 커넥션이 풀에 남지 않도록 정리 설정을 건다. + HttpClientBuilder builder = HttpClients.custom().setConnectionManager(connectionManager) + .evictExpiredConnections() + .evictIdleConnections(TimeValue.ofSeconds(IDLE_CONNECTION_EVICT_SECONDS)); if (!contentCompression) { builder.disableContentCompression(); }