TCP 에서 keep-alive

A "keep-alive" mechanism periodically probes the other end of a connection when the connection is otherwise idle, even when there is no data to be sent.
RFC 1122

공부하면서 꽤 재밌었던 부분이다. 내가 생각했던 TCP keep-alive와 다르게 동작했기 때문이다.

내가 생각했던 keep-alive는 연결이 끊어지지 않도록 주기적으로 핑퐁 메시지를 보내 연결을 유지하는 것이였다. 어찌보면 틀린말은 아니지만 본질적인 keep-alive 매커니즘은 이게 아니다.

TCP keep-alive의 본질적인 목적은 피어의 상태를 확인하고, 피어의 상태에 따라 조치를 취하는것이다.

만약 피어와 연결이 끊어진것이라면, 내쪽에서도 TCP연결을 끊어 자원을 release 해주고, 연결되어있는 상태라면 연결을 지속시켜주는것이다.

TCP 의 연결 종료

https://users.cs.northwestern.edu/~agupta/cs340/project2/TCPIP_State_Transition_Diagram.pdf

TCP 연결 종료는 한쪽이 FIN 패킷을 보내고, 다른 한쪽이 그것을 받고 ACK을 보냄으로써 연결이 끊기게 된다. 하지만 현실세계의 네트워크는 여러가지 이유로 이 흐름대로만 흘러가진 않고, 비정상적인 종료, 네트워크 문제등으로 실제 커넥션은 종료되었지만 TCP 커넥션은 종료되지 않고 남아있을 수 있다.

keep-alive의 목적중 하나는 이러한 죽은 피어를 확인하고 조치를 취하는것이다.

keep-alive

위 설명대로 실제론 연결이 종료되었지만, TCP는 끊어지지 않은채 남아있을 수 있다. 이때 keep-alive는 이러한 커넥션들을 탐색해 조치를 취한다. 다음은 keep-alive에 사용되는 3가지 커널 파라미터다

  • net.ipv4.tcp_keepalive_time : keepalive 매커니즘이 수행되기까지 기다리는 시간이다.
    • default 2시간(7200) 으로 설정
  • net.ipv4.tcp_keepalive_intvl : keepalive 매커니즘이 수행될때, 피어의 상태를 확인하기 위한 주기
    • 기본 75초(75)로 설정
  • net.ipv4.tcp_keepalive_probes : 커넥션이 끊어졌다고 판단하기 위한 시도 횟수
    • 기본 9회(9)로 설정

즉 한 커넥션에 대해 마지막 메시지 이후 2시간동안 주고받은 메시지가 없다면, 75초를 주기로 상대방이 살아있나 메시지를 보낸다. 이때 9회동안 상대로부터 응답이 없다면 커넥션을 종료하게 된다.

이렇게 주기적으로 피어에게 메시지를 보내 연결이 끊어진 커넥션들을 정리하고, 방화벽등이 통신하지 않는 idle 커넥션을 정리하는것을 방지해 연결을 지속시켜준다.

장단점

keep-alive를 사용하면 연결을 확인하고, 문제가 발생하면 연결을 끊음으로써 자원을 release 해줌으로써, 불필요한 자원점유를 방지해줄 수 있다. 하지만 keep-alive를 통해 주기적인 패킷을 보내는것으로 네트워크상 트래픽이 많아질 수 있으며 이로인해 keep-alive의 기본 설정은 off로 되어있다.

HTTP 에서 keep-alive

HTTP에서 keep-alive의 목적은 TCP 커넥션을 재사용하는것이다.

기본적으로 keep-alive 설정은 꺼져있으며, 헤더에 Connection: keep-alive 를 같이 보내 keep-alive를 설정할 수 있다. 또한 서버에서 이 설정을 받아줄 수 있어야 하며, 서버에서 keep-alive 설정이 되어있지 않다면, 아무리 보내도 TCP 커넥션을 유지할 수 없다.

❗ 많은 글들에서 HTTP/1.1 에선 keep-alive가 기본으로 설정되었다고 하는데 내가 직접 실험해본 결과로는 Connection: keep-alive 헤더를 넣어야지만 keep-alive 설정이 켜졌다.

많이 사용하는 HTTP툴인 PostMan은 기본으로 keep-alive를 켜고 요청을 보낸다.


클라이언트 헤더 Connection: keep-alive 세팅

만약 서버에서 keep-alive를 받아줄 수 있다면, 다음과 같은 헤더를 리턴해준다.


Response 헤더 Keep-Alive: timeout=5 세팅

timeout=5 또는 쉼표로 구분되어 최대 요청수라는 max라는 값이 같이 올 수 있다

Keep-Alive: timeout=5,max=1000

HTTP 레벨에서 keep-alive가 설정되면, tcp로 keep-alive 메시지가 오가며 timeout이 지나면 연결이 끊어진다.


timeout 5초 이후 연결이 끊어짐 (서버에서 RST 패킷 전송)

또한 keep-alive가 유지되는 동안 동일 호스트로 요청을 보내면 동일한 TCP 커넥션을 사용한다.

(포트가 동일하며 TCP handshake가 없음)


동일 TCP 커넥션으로 HTTP 메시지 전송

만일 Connection: keep-alive 헤더를 넣지 않으면 매 요청마다 TCP 연결이 새로 맺어진다.


http 데이터 전송 이후 FIN 패킷으로 TCP 커넥션이 종료되는 모습.

결론

언뜻보면 두 기능은 비슷해보인다. 하지만 두 기능은 다른 프로토콜 레이어에서 동작하는 기능이며, 서로 전혀 상관없는 다른 동작이다.

TCP keep-alive의 목적은 죽은 TCP 커넥션을 정리하는데 목적이 있다. HTTP 뿐만 아니라 TCP를 사용하는 모든 상위 레이어에서 사용한 TCP 커넥션중 비정상적으로 남아있는 커넥션을 확인해 종료하는것이 TCP keep-alive의 목적이다.

HTTP keep-alive의 목적은 TCP 커넥션을 재사용해 비용을 줄이는것이다. 하나의 TCP 커넥션을 통해 여러번의 HTTP 요청을 보내 TCP 핸드쉐이크 및 TLS 핸드쉐이크에 대한 비용을 줄이는것이 목적이다.

profile
김재현입니다.

0개의 댓글

관련 채용 정보

Powered by GraphCDN, the GraphQL CDN