RestTemplate 설정 추가 및 연결 풀, 타임아웃, Rate Limiting 기능 적용
parent
36570132e4
commit
e782cdac5c
@ -1,25 +1,112 @@
|
||||
package egovframework.config;
|
||||
|
||||
import com.google.common.util.concurrent.RateLimiter;
|
||||
import egovframework.configProperties.RestTemplateProperties;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* RestTemplate 설정 클래스
|
||||
* 외부 API 호출을 위한 RestTemplate Bean 설정
|
||||
* Apache HttpClient 4 기반 연결 풀 관리 및 타임아웃 설정
|
||||
* Rate Limiting: application.yml의 설정값 사용
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class RestTemplateConfig {
|
||||
|
||||
private final RestTemplateProperties properties;
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
|
||||
public RestTemplate restTemplate(RestTemplateBuilder builder) {
|
||||
// 타임아웃 설정 (application.yml에서 읽어옴)
|
||||
int connectTimeout = properties.getTimeout().getConnectTimeoutMillis();
|
||||
int readTimeout = properties.getTimeout().getReadTimeoutMillis();
|
||||
|
||||
log.info("RestTemplate 타임아웃 설정 - connectTimeout: {}ms, readTimeout: {}ms",
|
||||
connectTimeout, readTimeout);
|
||||
|
||||
RequestConfig requestConfig = RequestConfig.custom()
|
||||
.setConnectTimeout(connectTimeout)
|
||||
.setSocketTimeout(readTimeout)
|
||||
.setConnectionRequestTimeout(connectTimeout)
|
||||
.build();
|
||||
|
||||
// 연결 풀 관리 설정 (application.yml에서 읽어옴)
|
||||
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
|
||||
cm.setMaxTotal(properties.getConnectionPool().getMaxTotal());
|
||||
cm.setDefaultMaxPerRoute(properties.getConnectionPool().getMaxPerRoute());
|
||||
|
||||
log.info("RestTemplate 연결 풀 설정 - maxTotal: {}, maxPerRoute: {}",
|
||||
properties.getConnectionPool().getMaxTotal(),
|
||||
properties.getConnectionPool().getMaxPerRoute());
|
||||
|
||||
CloseableHttpClient httpClient = HttpClientBuilder.create()
|
||||
.setDefaultRequestConfig(requestConfig)
|
||||
.setConnectionManager(cm)
|
||||
.build();
|
||||
|
||||
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
|
||||
|
||||
// RestTemplate 생성
|
||||
RestTemplate restTemplate = builder
|
||||
.requestFactory(() -> requestFactory)
|
||||
.build();
|
||||
|
||||
// Rate Limiter 인터셉터 추가 (0 이하면 제한 없음)
|
||||
double permitsPerSecond = properties.getRateLimit().getPermitsPerSecond();
|
||||
if (permitsPerSecond > 0) {
|
||||
log.info("RestTemplate Rate Limiter 활성화 - 초당 {} 건으로 제한", permitsPerSecond);
|
||||
restTemplate.getInterceptors().add(new RateLimitInterceptor(permitsPerSecond));
|
||||
} else {
|
||||
log.info("RestTemplate Rate Limiter 비활성화 - 제한 없음");
|
||||
}
|
||||
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate Limiting 인터셉터
|
||||
* Guava의 RateLimiter를 사용하여 요청 속도 제한
|
||||
*/
|
||||
private static class RateLimitInterceptor implements ClientHttpRequestInterceptor {
|
||||
private final RateLimiter rateLimiter;
|
||||
private final double permitsPerSecond;
|
||||
|
||||
// 타임아웃 설정 (30초)
|
||||
factory.setConnectTimeout(30000);
|
||||
factory.setReadTimeout(30000);
|
||||
public RateLimitInterceptor(double permitsPerSecond) {
|
||||
this.permitsPerSecond = permitsPerSecond;
|
||||
this.rateLimiter = RateLimiter.create(permitsPerSecond);
|
||||
log.debug("RateLimitInterceptor 생성 - 초당 {} 건", permitsPerSecond);
|
||||
}
|
||||
|
||||
return new RestTemplate(factory);
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
|
||||
ClientHttpRequestExecution execution) throws IOException {
|
||||
// 요청 전에 Rate Limiter를 통해 대기
|
||||
double waitTime = rateLimiter.acquire();
|
||||
if (waitTime > 0) {
|
||||
log.debug("Rate Limiter 대기 - {}초 대기 후 요청: {} {}",
|
||||
String.format("%.3f", waitTime),
|
||||
request.getMethod(),
|
||||
request.getURI());
|
||||
}
|
||||
return execution.execute(request, body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
package egovframework.configProperties;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* RestTemplate 관련 설정 속성
|
||||
* application.yml의 rest-template 설정값을 읽어옴
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "rest-template")
|
||||
public class RestTemplateProperties {
|
||||
|
||||
private Timeout timeout = new Timeout();
|
||||
private ConnectionPool connectionPool = new ConnectionPool();
|
||||
private RateLimit rateLimit = new RateLimit();
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class Timeout {
|
||||
private int connectTimeoutMillis = 10000; // 기본값: 10초
|
||||
private int readTimeoutMillis = 10000; // 기본값: 10초
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class ConnectionPool {
|
||||
private int maxTotal = 100; // 기본값: 최대 100 연결
|
||||
private int maxPerRoute = 20; // 기본값: 경로당 20 연결
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class RateLimit {
|
||||
private double permitsPerSecond = 5.0; // 기본값: 초당 5건
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue