1. TIME_WAIT 소켓의 문제점과 대안
패킷 유실에 따른 비정상적인 통신흐름이 발생할 수 있다. 가령, 아래 그림과 같이 마지막 ACK가 중간에 유실될 경우 FIN에 대한 ACK를 받지 못했기에 LAST-ACK상태이고, SYN 요청에 대해 RST를 보낸다.
반면, TIME_WAIT이 있을 경우에는 이상을 감지하여 한번 더 FIN 패킷을 요청한다.
active closer(먼저 연결을 끊는) 쪽에서 TIME_WAIT 소켓이 생성된다.
TIME_WAIT 소켓이 많아지면, 로컬의 포트 고갈에 따른 애플리케이션 타임아웃, 잦은 TCP connection 생성/해제로 인해 서비스의 응답속도가 낮아진다. (TIME_WAIT 소켓의 기본 타임아웃은 1분이다.)
1) 클라이언트 입장
커널 파라미터(tcp_tw_reuse)를 사용하여 TIME_WAIT 소켓을 재사용할 수 있다. 하지만 불필요한 TCP 3way handshake가 일어날 수 있으므로 요청할 때마다 소켓을 새로 연결하는 방식(Connectionless)가 아닌, 미리 소켓을 열어놓고 처리하는 방식(Connection Pool)으로 해결할 수 있다.
2) 서버 입장
keep alive 기능을 켬으로써 불필요한 3 way handshake를 줄이고 TIME_WAIT 소켓도 줄일 수 있다. keep alive란, 한번 맺은 세션을 요청이 끝나더라도 유지해주는 기능이다. 보다 구체적으로는, keepalive timeout 시간이 지나면(tcp_keepalive_time 설정값) 서버에서 Keepalive 확인 패킷을 보낸다. 이 패킷에 대한 응답을 받으면 타이머는 원래 값으로 돌아가 다시 카운트를 진행한다. 반면 응답을 받지 못한 경우 tcp_keepalive_intvl에 정의된 시간만큼 경과한 후 요청을 다시 보내게 되고, tcp_keepalive_probes에 정의된 횟수만큼 보낸다. 이 후로도 응답이 없을 경우 클라이언트는 연결이 끊어졌다고 인지하고 서버에 RST 패킷을 보낸 다음 자신의 소켓을 닫는다. (그리하여 Keepalive를 사용할 경우 연결이 끊어졌음에도 FIN 패킷을 받지 못해 정리되지 않고 남아있던 좀비 커넥션을 없애는 효과도 발생한다.)
2. HTTP Keepalive
HTTP Keepalive는 설정된 기간까지만 Connection을 유지하다 FIN을 서버에서 먼저 날린다. Connection을 유지하기 위해서 클라이언트는 요청에 Connection:Keep-Alive 헤더를 포함시킨다.(HTTP/1.0) HTTP/1.1에서는 해당 설정이 빠졌고, 기본적으로 활성화되어 있다. Connection 종료시에 Connection: close헤더를 명시해야 한다.
즉, TCP keepalive는 서버간에 ACK 패킷을 보내 세션 테이블이 지워지지 않고 계속 세션 정보를 유지하는데 반해, HTTP Keepalive는 일정시간이 지나면 능동적으로 연결을 끊는다. 이에 TCP Keepalive는 mq, kafka 등 TCP 기반의 서비스들을 대상으로 지속적 연결을 유지해야 하는 경우 사용되고, HTTP Keepalive는 Apache, Nginx 등 웹 애플리케이션에서 설정된 기간까지 최대한 연결을 유지하기 위해 사용된다. (두 설정을 모두 적용했을 경우 HTTP Keepalive가 능동적으로 FIN 패킷을 보내기에 이 설정대로 동작한다.)
3. keep-alive 제한과 규칙
•
클라이언트가 요청에 Connection: close 헤더를 포함해 보냈으면, 그 커넥션으로 추가적인 요청을 보낼 수 없다.
•
메시지가 자신의 길이 정보를 정확히 가지고 있을 때에만 Connection을 지속시킬 수 있다. 예를 들어 Entity body는 Content-Length를 가지거나 chunked transfer encoding으로 인코드되어 있어야 한다.
•
HTTP/1.1 프락시는 클라이언트와 서버 각각에 대해 별도의 keepalive connection을 맺고 관리해야 한다.