
HttpClient์์ ์ธ๋ถ ์ค์ ํ ํ, ์ด๋ฅผ WebClient์ ์ฃผ์
| ๊ตฌ๋ถ | ๐ป WebClient | ๐ก HttpClient |
|---|---|---|
| ๋ ๋ฒจ | High-level | Low-level |
| ๊ธฐ๋ฐ | Spring WebFlux | reactor-netty |
| ์ง๋ ฌํ | ์๋(Jackson ๋ฑ) | ์๋(์ง์ ํ์ฑ) |
| ์ปค์คํฐ๋ง์ด์ง | ์ ํ์ (์ปค๋ฅํฐ ํตํด ๊ฐ๋ฅ) | ์์ ๋ ๋์ |
| ๋ฌ๋์ปค๋ธ | ์ค | ์ |
| ๊ถ์ฅ ์ฉ๋ | ์ผ๋ฐ REST API ํธ์ถ | ๋คํธ์ํฌ ์ธ๋ถ ํ๋, ํน์ ์ํฉ |
์ฅ์ โ
๋จ์ โ ๏ธ
HttpClient ์์กด์ฅ์ โ
๋จ์ โ ๏ธ
/**
* ๊ณตํต ์ปค๋ฅ์
ํ ์ค์ (์ปค๋ฅ์
์ฌ์ฌ์ฉ์ ์ํจ)
*/
@Bean
public ConnectionProvider connectionProvider() {
return ConnectionProvider.builder("test-pool")
.maxConnections(100)
.pendingAcquireMaxCount(500)
.pendingAcquireTimeout(Duration.ofSeconds(30))
.build();
}
/**
* Webclient ์ ์ (ReactorClientHttpConnector ํ์ฉํ์ฌ
*/
@Bean
public HttpClient createHttpClient(ConnectionProvider connectionProvider) {
SslContext sslContext;
try {
sslContext = SslContextBuilder.forClient().build();
} catch (SSLException e) {
throw new CustomRuntimeException(ErrorEnum.SSL_CONTEXT_ERROR);
}
return HttpClient.create(connectionProvider)
.secure(sslContextSpec -> sslContextSpec.sslContext(sslContext).handshakeTimeout(Duration.ofSeconds(30)))
.responseTimeout(Duration.ofSeconds(90))
.doOnConnected(conn -> conn
.addHandlerLast(new ReadTimeoutHandler(90))
.addHandlerLast(new WriteTimeoutHandler(5)));
}
/**
* ๊ณตํต HttpClient (์๋ต ํ์์์ ์ค์ ํฌํจ)
*/
@Bean
public WebClient testWebClient(HttpClient httpClient) {
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory("https://example.com/test");
factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
//WebClient ๊ธฐ๋ณธ ๋ฒํผ๊ฐ 256KB ๋๋ฌธ์ ์ด๊ณผํ๋ฉด BufferLimitException ๋ฐ์
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.uriBuilderFactory(factory)
.baseUrl("https://example.com/test")
.exchangeStrategies(ExchangeStrategies.builder()
.codecs(configurer -> configurer
.defaultCodecs()
.maxInMemorySize(10 * 1024 * 1024)) // โ
์ต๋ 10MB
.build())
.build();
}
@Component
@RequiredArgsConstructor
public class TestProvider {
private final WebClient testWebClient;
/**
* Mono ํ์ฉํ ๋น๋๊ธฐ ํธ์ถ
*/
private Mono<TestDTO> test() {
return testWebClient.get()
.uri(uriBuilder -> uriBuilder
.queryParam("serviceKey", "testKey")
.queryParam("pageNo", 1)
.queryParam("numOfRows", 1000)
.queryParam("returnType", "JSON")
.build())
.retrieve()
.bodyToMono(TestDTO.class)
.doOnError(TimeoutException.class, e -> log.error("โณ TimeoutException ๋ฐ์ : {}", e.getMessage()))
.doOnError(IllegalStateException.class, e -> log.error("โณ IllegalStateException ๋ฐ์ : {}", e.getMessage()))
.doOnError(WebClientResponseException.class, e -> log.error("โณ WebClientResponseException ๋ฐ์ : {}", e.getMessage()))
.retryWhen(Retry.fixedDelay(5, Duration.ofSeconds(5))
.filter(throwable -> throwable instanceof TimeoutException || throwable instanceof IllegalStateException || throwable instanceof WebClientResponseException)
.doBeforeRetry(retrySignal -> {
Throwable failure = retrySignal.failure();
log.warn("๐ [ ํ
์คํธ ์๋ํํฐ {}ํ์ฐจ ์ฌ์๋] ์ด์ : {}",
retrySignal.totalRetries() + 1,
failure.getMessage());
}));
}
}