티스토리 뷰

반응형

안녕하세요 Pingu입니다.🐧

 

오늘은 HTTP 통신을 할 때 사용하는 TCP/IP 통신에서 신뢰할 수 있는 통신을 위해 커넥션을 맺게 되는데 커넥션이 뭔지, 어떻게 맺게 되는지, TCP 커넥션에 의한 성능 저하 원인에 대해 알아보려고 합니다!

TCP 커넥션

먼저 TCP 커넥션을 사용하는 상황을 보며 언제 필요한지 알아보겠습니다.

 

예를 들어 icksw.tistory.com 라는 URL에 접속한다고 생각해보겠습니다. 이때 브라우저(클라이언트)와 서버에서 발생하는 일은 아래와 같습니다.

  1. 브라우저가 icksw.tistory.com라는 호스트 명을 추출한다.
  2. 브라우저가 호스트 명에 대한 IP주소를 찾는다.
  3. 브라우저가 포트 번호(80)를 얻는다.
  4. 브라우저가 아까 찾은 IP의 80 포트로 TCP 커넥션을 생성한다.
  5. 브라우저가 서버로 HTTP GET 요청 메시지를 보낸다
  6. 브라우저가 서버에서 온 HTTP 응답 메시지를 읽는다.
  7. 브라우저가 TCP 커넥션을 끊는다.

위와 같은 과정을 통해 우리는 icksw.tistory.com이라는 주소의 화면 정보를 얻게 됩니다.

 

TCP 통신은 신뢰할 수 있는 통신 방법으로 주고받는 데이터를 IP 패킷이라고 불리는 것으로 쪼개서 주고받게 되는데, IP 패킷은 다음과 같이 구성됩니다.

  • IP 패킷 헤더 (보통 20 바이트)
    • 발신지 IP, 목적지 IP, 데이터의 크기, 기타 플래그
  • TCP 세그먼트 헤더 (보통 20 바이트)
    • TCP 포트 번호, TCP 제어 플래그, 데이터 순서, 무결성을 위한 숫자 값
  • TCP 데이터 조각 (0바이트 이상)
    • 요청한 데이터

위와 같은 IP 패킷의 정보들 중 <발신지 IP, 발신지 포트 번호, 목적지 IP, 목적지 포트번호>로 유일한 TCP 커넥션을 생성하게 됩니다. 또한 아무리 작은 데이터라도 40바이트 정도 되는 패킷 헤더, 세그먼트 헤더를 반드시 포함해야한다는 점도 기억해주세요!

커넥션 발신지 IP 발신지 포트 번호 목적지 IP 목적지 포트 번호
A 209.1.32.34 2034 204.62.128.58 4140
B 209.1.32.35 3227 204.62.128.58 4140
C 209.1.32.35 3105 207.25.71.25 80

위와 같이 유일한 커넥션들이 만들어질 수 있습니다. 물론 커넥션들이 같은 목적지 IP, 포트번호를 가질 수도 있고 같은 발신지 IP를 가질 수도 있지만 절대로 4개의 구성요소가 같은 커넥션은 있을 수 없습니다.

TCP 커넥션 성능

그럼 이제 TCP 커넥션에 대해 알게 되었으니 성능에 대해 알아보겠습니다. HTTP는 Application 계층으로 그 아래에 있는 Transport 계층인 TCP 계층의 성능에 영향을 받게 됩니다. 따라서 TCP 성능이 좋아지면 HTTP을 사용하는 프로그램의 성능도 좋아지게 되기 때문에 아주 중요하다고 할 수 있습니다!

 

일단 HTTP 요청 과정에서 TCP 커넥션에 의해 어떻게 지연되는지 살펴보겠습니다.

 

위와 같이 한 번의 HTTP 통신이 처리되게 되는데, 너무 많은 데이터를 주고받는 것이 아닌 이상 위의 상황에서 지연은 대부분 TCP 네트워크로 인해 발생합니다. 원인들을 정리해보면 아래와 같습니다.

  • URI에서 IP 주소, 포트번호를 찾는데 걸리는 시간
  • TCP 커넥션을 맺는데 발생하는 시간
  • 클라이언트가 HTTP 요청을 서버에 보내고 이를 서버가 처리하고 응답하는 시간

위와 같은 원인 들 중 이번에는 아래와 같이 TCP에 관련된 지연에 대해서 알아보도록 하겠습니다.

  • TCP 커넥션의 Hand Shake 설정
  • TCP piggyback(편승), acknowledgment(확인 응답)을 위한 알고리즘
  • TCP Slow Start(느린 시작)
  • 데이터를 모아서 한 번에 전송하는 Nagle 알고리즘
  • TIME_WAIT 지연과 포트 고갈

 

TCP 커넥션의 Hand Shake 설정

TCP를 사용하여 데이터를 전송할 때 커넥션을 맺기 위해 IP 패킷을 몇 번 주고받게 되는데, 이러한 과정을 Hand Shake라고 하고 과정은 아래와 같습니다.

  1. 클라이언트가 새로운 TCP 커넥션을 위해 TCP 패킷을 서버에 보냅니다. 이때 SYN이라는 플래그를 함께 보내게 되며 이는 커넥션 생성 요청이라는 의미를 가집니다.
  2. 서버가 SYN이라는 플래그를 가진 패킷을 받으면 커넥션 매개변수들을 만들어서 커넥션이 맺어졌다는 것을 의미하는 SYN, ACK 플래그를 포함한 패킷을 클라이언트에게 보냅니다.
  3. 클라이언트는 SYN, ACK라는 플래그를 가진 패킷을 받고 커넥션이 맺어졌다는 것을 알게 되며, 이를 확인했다는 의미로 ACK 플래그를 포함하여 서버에 패킷을 보냅니다. 이때 HTTP 요청도 함께 보낼 수 있습니다.

즉 위의 그림에서 볼 수 있듯 한 번의 데이터를 주고받기 위해 커넥션을 맺는데 많은 시간을 사용하는 것을 볼 수 있습니다. 커넥션을 매번 데이터를 주고받을 때마다 생성한다면 정말 비효율적이기 때문에 HTTP는 이러한 커넥션을 끊지 않고 유지하여 시간을 절약하게 됩니다.

확인 응답 지연

TCP 통신은 데이터 무결성을 보장하기 때문에 이를 위한 체크를 해줘야 합니다. 패킷들이 여러 개로 쪼개져서 전송되기 때문에 패킷들의 순서와 전송하는 동안 유실되지는 않았는지 확인해줘야 합니다. 이를 위해 패킷 수신자는 패킷을 온전하게 받으면 확인 응답 패킷을 송신자에게 반환하게 됩니다. 만약 송신자가 패킷을 보내고 일정 시간이 지나도 확인 응답 패킷이 오지 않는다면 뭔가 문제가 발생한 것으로 간주하고 패킷을 다시 전송하게 됩니다. 이러한 확인 응답은 크기가 작기 때문에 데이터 패킷에 함께 piggyback(편승)하여 전송하여 좀 더 효율적으로 처리하게 됩니다.

TCP의 Slow Start

TCP의 전송 속도는 TCP 커넥션이 만들어진지 얼마나 지났는지에 따라 달라질 수 있습니다. TCP 커넥션은 시간이 지나면서 전송 속도를 계속 변화시키게 되며 처음엔 속도를 제한하다가 점차 속도를 올리게 됩니다. 이러한 것을 TCP Slow Start라고 하며 이는 급작스러운 부하를 방지하기 위해 쓰입니다. 

 

TCP Slow Start는 처음에 커넥션이 맺어질 땐 TCP가 한 번에 전송할 수 있는 패킷의 수를 제한하게 되며 전송이 성공하면 전송 패킷 수를 증가시킵니다. 즉 확인 응답을 받을 때마다 전송할 수 있는 패킷 수를 증가시키며 이 때문에 방금 만들어진 TCP 커넥션보다 어느 정도 시간이 지난 TCP 커넥션이 더 빠른 전송 속도를 갖게 됩니다. 물론 이를 위해서는 커넥션을 유지하도록 만들어야 합니다!

네이글(Nagle) 알고리즘과 TCP_NODELAY

TCP는 1바이트와 같은 작은 데이터라도 애플리케이션에서 전송할 수 있게 해 주는데, 만약 매번 1바이트의 데이터만 가진 패킷을 보내게 된다면 기본적으로 패킷의 헤더가 40바이트 정도 되기 때문에 매우 비효율적입니다. 따라서 효율적인 데이터 정송을 위해 Nagle 알고리즘을 사용하게 되는데요, Nagle 알고리즘은 패킷을 전송하기 전에 어느 정도 크기가 될 때까지 모았다가 한 번에 전송하는 알고리즘입니다.

 

하지만 만약 현재 보내는 패킷이 마지막인데도 불구하고 패킷의 크기가 작다고 보내지 않을 경우 영원히 해당 패킷이 전송되지 않을 수 있기 때문에 모든 패킷이 확인 응답을 받았다면 크기가 작다고 해도 패킷을 전송할 수 있게 해 줍니다. 이러한 부분 때문에 지연이 발생하며 특히 Nagle 알고리즘은 확인 응답 지연과 함께 사용하면 성능이 매우 나빠집니다.

 

따라서 HTTP 애플리케이션은 Nagle 알고리즘을 비활성화할 수도 있는데요, 이 때 사용하는것이 TCP_NODELAY입니다. 이 값을 사용하면 Nagle 알고리즘을 비활성화 할 수 있습니다.

TIME_WAIT의 누적과 포트 고갈

TIME_WAIT 누적과 포트 고갈은 성능 측정을 할 때는 성능 저하를 보이지만 실제 상황에서는 문제를 발생하지 않습니다. TCP 커넥션의 에지에서 TCP 커넥션을 끊으면 에지에서는 커넥션의 IP 주소와 포트번호를 메모리에 기록해둡니다. 이렇게 저장해두는 이유는 같은 IP 주소와 포트번호를 사용하는 TCP 커넥션이 일정 시간 동안에는 생성되지 않게 하기 위해서인데, 보통 패킷의 최대 생명주기의 2배(2 MSL이라고 합니다.)인 2분 정도만 유지됩니다. 만약 이렇게 해주지 않으면 동일한 정보의 새로운 커넥션에 기존에 존재하던 패킷이 삽입되어 패킷이 중복될 수 있고 TCP 데이터가 충돌할 수 있습니다.

 

2분 동안 동일한 커넥션을 만들 수 없기 때문에 만약 발신지 포트가 최대 6만개를 사용할 수 있고 2분동안 커넥션이 재사용될 수 없다면 초당 60000 / 120 = 500개의 커넥션이 제한되게 됩니다. 서버가 초당 500개의 트랜잭션을 처리할 만큼 빠르지 않다면 TIME_WAIT 포트 고갈 문제는 발생하지 않습니다. 물론 그렇다고 하더라도 커넥션을 너무 많이 맺거나 커넥션 정보를 메모리에 너무 많이 저장하는 것은 좋지 않기 때문에 주의해야 합니다.

 

 

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함