Webclient Timeout 설정

로로·2023년 10월 8일
0

외부기관 API를 호출할 때 응답에 대한 timeout을 설정해줘야했다

HttpClient 또는 Webclient를 생성할 때 설정해도 되지만,
우리는 API건마다 다른 timeout을 가져가야하기 때문에 호출 시점에 timeout을 설정하는 방법을 선택했다

두 가지 방법이 있는데 ..
1. HttpClientRequest
2. Reactor Core


1. HttpClientRequest

webClient.get()
  .uri("https://baeldung.com/path")
  .httpRequest(httpRequest -> {
    HttpClientRequest reactorRequest = httpRequest.getNativeRequest();
    reactorRequest.responseTimeout(Duration.ofSeconds(2));
  });

2. Reactor Core

webClient.get()
  .uri("https://baeldung.com/path")
  .retrieve()
  .bodyToFlux(JsonNode.class)
  .timeout(Duration.ofSeconds(5));

실습

👉🏻 실습전 timeout 테스트 API 생성

@RequestMapping(value = "/timeout")
    public ResponseEntity<?> timeout() throws InterruptedException {
        log.info("timeout test");
        Thread.sleep(5000);
        log.info(LocalDateTime.now().toString());

        return ResponseEntity.ok().build();
    }

1. Http Client 생성

HttpClient httpClient = HttpClient.create();

2. WebClient 생성

WebClient client = WebClient.builder()
                .baseUrl("http://localhost:8080")
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .filter(
                        (req, next) -> next.exchange(
                                ClientRequest.from(req).header("from", "webclient").build()
                        )
                )
                .filter(
                        ExchangeFilterFunction.ofRequestProcessor(
                                clientRequest -> {
                                    log.info(">>>>>>>>>> REQUEST <<<<<<<<<<");
                                    log.info("Request: {} {}", clientRequest.method(), clientRequest.url());
                                    clientRequest.headers().forEach(
                                            (name, values) -> values.forEach(value -> log.info("{} : {}", name, value))
                                    );
                                    return Mono.just(clientRequest);
                                }
                        )
                )
                .filter(
                        ExchangeFilterFunction.ofResponseProcessor(
                                clientResponse -> {
                                    log.info(">>>>>>>>>> RESPONSE <<<<<<<<<<");
                                    clientResponse.headers().asHttpHeaders().forEach((name, values) -> values.forEach(value -> log.info("{} : {}", name, value)));
                                    return Mono.just(clientResponse);
                                }
                        )
                )
                .build();
  • filter를 설정해서 요청과 응답에 대한 로그도 찍어볼 수 있다

3. API 호출

// HttpClientRequest 사용
Mono<?> stringMono = client
                .get()
                .uri("/timeout")
                .httpRequest(httpRequest -> {
                    HttpClientRequest reactorRequest = httpRequest.getNativeRequest();
                    reactorRequest.responseTimeout(Duration.ofSeconds(timeout));
                })
                .exchangeToMono(clientResponse -> {
                    log.info("Success!");
                    return clientResponse.toEntity(ResponseDto.class);
                })
  • timeout을 파라미터로 받아서 api 호출할때마다 설정해주면 끝 !
// Reactor Core 사용
Mono<?> stringMono = client
                .get()
                .uri("/timeout")
                .exchangeToMono(clientResponse -> {
                    log.info("Success!");
                    return clientResponse.toEntity(ResponseDto.class);
                })
                .timeout(Duration.ofSeconds(timeout))

결과

  • 5초 sleep, timeout 6초 설정해서 성공인 경우
2023-10-08T16:13:41.130+09:00  INFO 79925 --- [nio-8080-exec-1] com.example.localtest.Controller         : test
2023-10-08T16:13:41.130+09:00  INFO 79925 --- [nio-8080-exec-1] com.example.localtest.Controller         : 2023-10-08T16:13:41.130468
2023-10-08T16:13:41.202+09:00  INFO 79925 --- [nio-8080-exec-1] com.example.localtest.Controller         : >>>>>>>>>> REQUEST <<<<<<<<<<
2023-10-08T16:13:41.202+09:00  INFO 79925 --- [nio-8080-exec-1] com.example.localtest.Controller         : Request: GET http://localhost:8080/timeout
2023-10-08T16:13:41.202+09:00  INFO 79925 --- [nio-8080-exec-1] com.example.localtest.Controller         : from : webclient

2023-10-08T16:13:41.395+09:00  INFO 79925 --- [nio-8080-exec-2] com.example.localtest.Controller         : timeout test
2023-10-08T16:13:46.401+09:00  INFO 79925 --- [nio-8080-exec-2] com.example.localtest.Controller         : 2023-10-08T16:13:46.401034
2023-10-08T16:13:46.428+09:00  INFO 79925 --- [ctor-http-nio-2] com.example.localtest.Controller         : >>>>>>>>>> RESPONSE <<<<<<<<<<
2023-10-08T16:13:46.431+09:00  INFO 79925 --- [ctor-http-nio-2] com.example.localtest.Controller         : Date : Sun, 08 Oct 2023 07:13:46 GMT
2023-10-08T16:13:46.431+09:00  INFO 79925 --- [ctor-http-nio-2] com.example.localtest.Controller         : content-length : 0
2023-10-08T16:13:46.431+09:00  INFO 79925 --- [ctor-http-nio-2] com.example.localtest.Controller         : Success!
  • 5초 sleep, timeout 3초 설정해서 timeout이 발생한 경우
2023-10-08T16:16:01.816+09:00  INFO 79945 --- [nio-8080-exec-1] com.example.localtest.Controller         : test
2023-10-08T16:16:01.816+09:00  INFO 79945 --- [nio-8080-exec-1] com.example.localtest.Controller         : 2023-10-08T16:16:01.816840
2023-10-08T16:16:01.879+09:00  INFO 79945 --- [nio-8080-exec-1] com.example.localtest.Controller         : >>>>>>>>>> REQUEST <<<<<<<<<<
2023-10-08T16:16:01.879+09:00  INFO 79945 --- [nio-8080-exec-1] com.example.localtest.Controller         : Request: GET http://localhost:8080/timeout
2023-10-08T16:16:01.880+09:00  INFO 79945 --- [nio-8080-exec-1] com.example.localtest.Controller         : from : webclient

2023-10-08T16:16:02.064+09:00  INFO 79945 --- [nio-8080-exec-2] com.example.localtest.Controller         : timeout test
2023-10-08T16:16:05.070+09:00  WARN 79945 --- [ctor-http-nio-2] r.netty.http.client.HttpClientConnect    : [699c5c07-1, L:/127.0.0.1:62710 - R:localhost/127.0.0.1:8080] The connection observed an error

io.netty.handler.timeout.ReadTimeoutException: null
  • 응답 로그가 찍히지 않고 ReadTimeoutException이 발생하는 것을 볼 수 있다

👉🏻 error handler를 통해 Error를 찍어보면

HttpClientRequest를 사용시 WebClientRequestException ReadTimeoutExeption 발생
Reactor Core 사용시 TimeoutException 발생

Exception Handling은 나중에~.~


참고 : https://www.baeldung.com/spring-webflux-timeout

profile
청로하~🏝️

0개의 댓글

관련 채용 정보