<개요>
- WebClient 설정
- Timeout 설정
<내용>
1. Spring version update 따른 변경사항
Caused by: org.springframework.core.io.buffer.DataBufferLimitException: Exceeded limit on max bytes to buffer
- 해당 오류는 WebClient를 통해서 API Call후 받아오는 응답의 크기가 일정이상이 될 경우 발생한다.
현재 사용중인 API의 응답사이즈가 1~3MB 수준으로 조정이 필요한 상태이다.
a. 주의해야할 점이 yml 파일내에서 다음 옵션을 사용하여 조정이 가능한 버전이 있으나
spring.codec.max-in-memory-size=20MB
b. 지금 사용중인 버전에서는 동작하지 않아서 별도 설정을 적용하도록 하였다.
ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
.codecs(clientCodecConfigurer -> clientCodecConfigurer.defaultCodecs().maxInMemorySize(3* 1024 * 1024))
.build();
return WebClient.builder()
.exchangeStrategies(exchangeStrategies)
.baseUrl( )
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
이와 같이 WebClient builder내에 추가하여 정상동작 하는 것을 확인하였다.
2. Timeout 설정
- Spring WebClient를 사용할때 여러종류의 Timeout이 있다.
HttpClient httpClient = HttpClient.create()
.tcpConfiguration(
client -> client.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, -A- ) //miliseconds
.doOnConnected(
conn -> conn.addHandlerLast(new ReadTimeoutHandler( -B- , TimeUnit.MILLISECONDS))
.addHandlerLast(new WriteTimeoutHandler( -C- , TimeUnit.MILLISECONDS))
)
);
return WebClient.builder()
.exchangeStrategies(exchangeStrategies)
.baseUrl(webClientProperties.getBaseUri())
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
Mono<String> response = webClient
.get()
.uri(" ")
.retrieve()
.onStatus(HttpStatus::is4xxClientError, clientResponse -> Mono.error(new Exception())
.onStatus(HttpStatus::is5xxServerError, clientResponse -> Mono.error(new Exception())
.bodyToMono(String.class)
.timeout(Duration.ofMillis( -D- ))
.onErrorMap(ReadTimeoutException.class, e -> ))
.onErrorMap(WriteTimeoutException.class, e -> ))
A. Connection Timeout
- Client / Server 간의 Connection을 맺기 위해 소요되는 시간을 의미한다.
- Server에서 새로 Connection을 맺을 자원의 여유가 없다면 발생할 수 있다.
- HTTP Connection에 대한 내용이기 때문에 keep-alive 옵션역시 사용가능하다.
B. Read Timeout
- 데이터를 읽기 위해서 기다리는 시간을 의미한다.
- 내가 호출한 API 가 응답을 주지 못하면 발생할 수 있다.
- 너무 길다면? 적절히 설정해주지 않으면 응답을 받을때까지 계속 대기하게 되고, 자원이 고갈되는 현상이 발생한다.
- 너무 짧다면? 요청을 받은쪽에서는 처리가 되었으나, 응답을 기다리던 쪽에서는 Timeout이 발생하게 되어서 불일치 상태가 발생한다.
C. Write Timeout
- 데이터를 쓰기 위해서 기다리는 시간을 의미한다.
- 주어진 시간동안 Write Operation을 완료하지 못하면 발생할 수 있다.
- 즉, Connection 연결 후 데이터를 보내기 시작하였으나 해당시간보다 길어지게 되면 중단된다.
D. reactor timeout
- Reactive Stream은 Publisher, Subscriber, Subscription 을 통해서 비동기 / 넌블러킹 / back pressure 처리하는 개념이다.
- 우리가 다루는 Spring WebFlux는 reactive stream의 구현체로 reactor를 사용하고 있으며 Mono / Flux가 Publisher의 구현체이다.
- 따라서 Exception , Retry등을 처리할때도 기존 방식 대신 reactive stream의 기능을 활용해주는 것이 장점을 충분히 살릴 수 있는 방법이라고 생각한다.
- Spring WebFlux에서는 WebClient의 호출결과를 받았을때 결과 Body를 Mono로 감싸주어 데이터를 전달하는 형태가 되는데, 해당 시간동안 데이터를 전달하지 못하게 되면 timeout 이 발생하게 된다.
E. Transaction Timeout과 비교 (개인적인 생각 , 틀릴 수 있음)
- 우리가 일반적으로 DB transaction timeout을 설명할 때 Transaction Timeout > Statement Timeout > Socket Timeout 로 각 구간을 나누어서 설명하고 상위 Layer( ? 포장레이어?) 에서는 하위 Layer보다 크거나 같게 설정하는 것이 일반적이다.
- Web 호출 역시 비슷하게 살펴본다면 Publisher Timeout > Read/Write Timeout > Connection Timeout 정도로 비슷하게 정리해 볼 수 있지 않을까 생각했다.
<정리>
- MSA구조에서 각 API의 응답시간과 사이즈는 적절하게 설정해야 한다.
- 특히, 각 레벨에서의 적절한 수준의 timeout 설정은 필수이다.
- 너무 짧으면 많은 오류가 발생하게 되고, 이에 따른 side-effect (데이터 불일치, 로직의 복잡도 증가) 가 생기게 되며
- 너무 길거나 무제한으로 설정하게 되면 리소스 자원의 낭비가 발생한다.
'Java' 카테고리의 다른 글
Spring WebFlux를 활용한 Mutli API 호출통합 #1 (0) | 2022.11.04 |
---|---|
Geohash를 이용한 좌표기반 시스템 개선 (0) | 2022.07.08 |
ObjectMapper 사용시 주의점 (0) | 2022.05.18 |
OpendJDK 9 사용중 Troubleshooting (0) | 2022.02.17 |
TLSv1.3 on OpenJDK 11 Bug (0) | 2022.02.15 |