WebClient Connection reset by peer
WebClient로 타 API 요청 시
recvAddress(..) failed: Connection reset by peer
오류와 함께 요청이 정상적으로 처리되지 않는 문제가 발생하였습니다.
해당 오류를 처리하기 위해서는 일단 3 way handshake HTTP/1.1에 대한 이해가 필요합니다.
이해를 돕기위해 각각 간략하게 알아보겠습니다.
3 way handshake
TCP 통신의 경우 데이터를 주고받기 위해서 handshake 과정이 필요합니다.
1. 클라이언트는 서버에 접속을 요청하는 SYN 패킷을 보냅니다.
2. 서버는 SYN 패킷을 받고 클라이언트의 요청을 수락한다는 ACK와 SYN flag 가 설정된 패킷을 발송하고 클라이언트가 다시 응답하기를 기다립니다.
3. 클라이언트는 서버에게 ACK 패킷을 보내고 이때부터 연결이 이루어지고 데이터를 주고받습니다.
하지만 매 번 연결할 때마다 위의 과정을 거친다면? 좀 더 빠르게 통신 할 수 없게 됩니다.
HTTP 1.1 keep-alive
그래서 HTTP 1.0 에서 1.1로 버전이 바뀌면서 header의 Connection 속성이 나오게 됩니다.
header의 Connection 속성에 keep-alive 또는 close 를 값으로 전달해 줄 수 있는데 keep-alive로 요청을 보내고
서버의 응답 Connection 헤더에도 keep-alive 로 응답이 온다면 해당 커넥션을 유지하고 handshake 비용을 줄이고 재사용하게 됩니다.
그렇다면? 문제는 WebClient 의 HttpClient 설정에 있었습니다.
WebClient HttpClient 를 설정할 때 ConnectionProvider 클래스에 maxIdleTime 속성으로 해결할 수 있습니다.
ConnectionProvider provider = ConnectionProvider.builder("fixed")
.maxConnections(100)
.maxIdleTime(Duration.ofSeconds(20))
.maxLifeTime(Duration.ofSeconds(60))
.pendingAcquireTimeout(Duration.ofSeconds(60))
.evictInBackground(Duration.ofSeconds(120))
.build();
this.webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(HttpClient.create(provider)))
.build();
maxIdleTime 은 재사용하기 위해 유휴상태가 된 커넥션을 얼마나 유지할 것인가에 대한 속성인데
해당 속성이 서버의 값보다 크게되면? 서버에서는 해당 커넥션을 끊게 되지만 클라이언트는 알 수가 없어서 해당 커넥션으로 요청을 재시도하게 됩니다.
maxIdleTime의 default 값은 -1 로 무제한입니다.
해당 값을 20초 정도로 조절하여 만료시키고 다시 커넥션을 맺게 하여 해당 오류를 해결할 수 있었습니다.
아래는 ConnectionProvider 설정 값에 대한 설명입니다.
maxConnections : 유지할 Connection Pool의 수
maxIdleTime : 사용하지 않는 상태(idle)의 Connection이 유지되는 시간.
기본값: 무제한 (-1)
maxLifeTime: Connection Pool에서의 최대 수명 시간
기본값: 무제한 (-1)
pendingAcquireTimeout: Connection Pool에서 사용할 수 있는 Connection 이 없을 때 Connection을 얻기 위해 대기하는 시간
기본값 : 45초
pendingAcquireMaxCount: Connection을 얻기 위해 대기하는 최대 수
기본값 : 무제한 (-1)
evictInBackground: 백그라운드에서 만료된 connection을 제거하는 주기
lifo: 마지막에 사용된 커넥션을 재사용
fifo: 처음 사용된(가장 오래된) 커넥션을 재 사용