Web/Spring 2020. 2. 20. 00:46

 

오늘 다루어볼 내용은 Webflux에서 사용되는 WebClient가 onCancel이 호출되는 시점이다.  이번에 애플리케이션을 개발하면서 많은 서비스가 통신을 하는데, 필자가 개발한 애플리케이션의 jaeger 로그를 보니 WebClient가 cancelled("The subscription was cancelled") 되었다는 로그가 찍혀있었다. 무슨 이유로 이러한 로그가 남는지 확인했더니, 아래와 같은 이유였다.

 

"AServer -> BServer ->CServer"

 

이렇게 3개의 서버가 통신하는 상황인데, AServer의 Client는 ReadTimeOut이 3초이고, BServer는 ReadTimeOut이 5초이다. 그런데 CServer가 응답을 주는데 4초가 걸렸다면? 

 

이런 상황에서 발생하는 것이 cancelled 상황이다. AServer는 이미 Readtimeout이 나버려서 500 에러가 발생하였고, 이제 더이상 BServer는 응답을 받아올 필요가 없어졌으므로 subscribe를 cancel을 해버리는 것이다. 간단히 위와 같은 상황을 샘플 앱으로 작성해 보았다.

 

[AServer:9090]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Configuration
public class WebClientConfig {
 
    @Bean
    public WebClient.Builder webClientFactory(){
        TcpClient tcpClient = TcpClient.create()
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000)
                .doOnConnected(connection -> connection.addHandlerLast(new ReadTimeoutHandler(3000, TimeUnit.MILLISECONDS)));
 
        return WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient)));
    }
 
    @Bean
    public WebClient webClient() {
        return webClientFactory().build();
    }
 
}
 
@Slf4j
@RestController
@SpringBootApplication
@RequiredArgsConstructor
public class ExamApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(ExamApplication.class, args);
    }
 
    private final WebClient client;
 
    @GetMapping("/")
    public Mono<String> hello(){
        log.info("exam server accept request");
        return client.get("http://localhost:8080/")
                .retrieveBodyToMono(String.class);
}
 
 
 
cs

 

[BServer:8080]

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@Configuration
public class WebClientConfig {
 
    @Bean
    public WebClient.Builder webClientFactory(){
        TcpClient tcpClient = TcpClient.create()
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000)
                .doOnConnected(connection -> connection.addHandlerLast(new ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS)));
 
        return WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient)));
    }
 
    @Bean
    public WebClient webClient() {
        return webClientFactory().build();
    }
 
}
 
@Slf4j
@RestController
@SpringBootApplication
@RequiredArgsConstructor
public class ClientApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(ClientApplication.class, args);
    }
 
    private final WebServiceClient client;
 
    @GetMapping("/")
    public Mono<String> hello(){
        log.info("client server accept request");
        return client.get("http://localhost:7070/")
                .retrieveBodyToMono(String.class);
    }
 
}
 
 
cs

 

[CServer:7070]

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
@SpringBootApplication
public class ServerApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(ServerApplication.class, args);
    }
 
    @GetMapping("/")
    public String hello() throws InterruptedException {
        Thread.sleep(10000);
        return "hello";
    }
 
}
cs

 

이제 "http://localhost:9090/" 으로 요청을 보내보자!

 

 

보면 AServer(exam)은 ReadTimeOut Exception이 발생하였고, subscription was cancelled라는 메시지를 남기고 있다. 혹시나 이러한 문제가 발생한 사람이 있다면.. 적절히 서버들의 WebClient 옵션을 조정해줄 필요가 있거나 혹은 비정상적으로 응답이 느린 구간이 있을 수 있다. 하지만 때때로 클라이언트 애플리케이션에서 커낵션 풀을 사용하지 않아 과도하게 로컬 포트를 많이 개방한다거나 등의 문제로 cancelled되는 경우가 있으니, 여러가지 상황을 고려해보아야 한다.

 

마지막으로 ConnectionTimeOut, ReadTimeOut, SocketTimeOut의 차이점을 모른다면 아래 링크를 확인하자.

 

2019/02/12 - [프로그래밍언어/Java&Servlet] - Java - ConnectionTimeout,ReadTimeout,SocketTimeout 차이점?

 

Java - ConnectionTimeout,ReadTimeout,SocketTimeout 차이점?

Java - ConnectionTimeout,ReadTimeout,SocketTimeout 차이점? 사실 지금까지 웹개발을 해오면서 ConnectionTimeout,ReadTimeout,SocketTimeout에 대해 대략적으로만 알고있었지 사실 정확히 설명해봐라 혹은 차이..

coding-start.tistory.com

 

posted by 여성게
:

Java - ConnectionTimeout,ReadTimeout,SocketTimeout 차이점?


사실 지금까지 웹개발을 해오면서 ConnectionTimeout,ReadTimeout,SocketTimeout에 대해 대략적으로만 알고있었지 

사실 정확히 설명해봐라 혹은 차이점을 설명해봐라하면 대답하기 힘든 부분이 있었다. 이번 포스팅으로 

정확한 타임아웃 개념을 잡아보려고한다.

 

 

 

ConnectionTimeout이란?

ConnectionTimeout이라는 개념을 설명하기 전에 URL로 HTTP호출을 할 때 어떤 방식으로 수행되는지 이해가 필요하다.웹 브라우저가 네이버 서버에 접속하기 위해서 서버와 연결된 상태가 되어야한다. 보통 연결을 구성하기위해TCP Connection과 동일하게 3-way-HandShake 방식으로 수행하게 된다. 3-way HandShake가 정상적으로
수행하게 되면 웹 브라우저와 네이버 서버는 연결된 상태가 되는데, 이때까지 소요된 시간을 Connection에 소요된 시간이라고 할 수 있다. 

"즉,ConnectionTimeout이란 3-way HandShake가 정상적으로 수행되어 서버에 연결되기까지 소요된시간이다."

 

SocketTimeout이란?

클라이언트와 서버가 연결된 상태 이후에 서버는 페이지를 브라우저에 랜더링하기 위해 데이터를 전송한다. 이때 하나의 패킷으로데이터를 전송하는 것이 아니라 여러 개의 패킷으로 나눠서 전송하게 된다. 여러개의 패킷이 전송될 때, 각 패킷 간의 시간 Gap이생길 수 있는데 이 시간의 임계치를 SocketTimeout이라고 한다.

"즉, SocketTimeout이란 데이터를 여러개의 패킷으로 나누어 보낼때 각 패킷간의 시간 Gap을 이야기한다."

 


위의 두개를 그림으로 표현해보면,

 

결국 위의 두개의 시간설정은 URL을 호출 할때 꼭 필요한 설정이다. 만약 두 시간이 설정되지 않는다면?URL 접속 무한 대기가 발생될 수 있다.

 

ReadTimeout이란?

"Connection 맺은 후 Response(응답)을 받기까지 소요될 시간의 임계치이다."

 

사소하게 넘어 갈 수 있는 것들이지만 네트워크 상에 돌아가는 무엇인가를 개발한다면 꼭 숙지해야 될 것들중 하나인 것 같다.

 

posted by 여성게
: