[HTTP 완벽가이드] 4장 커넥션 관리

:D ·2023년 4월 25일
2

HTTP 완벽가이드

목록 보기
2/2
post-thumbnail

4.1 TCP 커넥션

http 통신은 tcp/ip를 통해 이루어진다. 일단 tcp 커넥션이 맺어지면 클라이언트와 서버 컴퓨터 간에 주고받는 메시지들은 손실 혹은 손상되거나 순서가 바뀌지 않고 안전하게 전달된다.

TCP는 HTTP에게 신뢰할 만한 통신 방식을 제공하고, TCP 커넥션의 한쪽에 있는 바이트들은 반대쪽으로 순서에 맞게 정확히 전달된다.

HTTP가 메시지를 전송하고자 할 경우, 현재 연결되어 있는 TCP 커넥션을 통해서 메시지 데이터 내용을 순서대로 보낸다. TCP는 세그먼트라는 단위로 데이터 스트림을 잘게 나누고, 세그먼트를 IP 패킷이라고 불리는 봉투에 담아서 인터넷을 통해 데이터를 전달한다.

4.2 TCP의 성능에 대한 고려

HTTP 트랜잭션 지연

HTTP는 TCP 바로 위에 있는 계층이기 때문에 HTTP 트랜잭션의 성능은 그 아래 계층인 TCP 성능에 영향을 받는다.

위의 그림으로 트랜잭션을 처리하는 과정을 보게되면 트랜잭션을 처리하는 시간은 TCP 커넥션을 설정하고, 요청을 전송하고, 응답 메시지를 보내는 것에 비하면 상당히 짧다.
클라이언트나 서버가 너무 많은 데이터를 내려받거나 복잡하고 동적인 자원들을 실행하지 않는 한, 대부분의 HTTP 지연은 TCP 네트워크 지연 때문에 발생한다.

  • DNS 찾기 : 클라이언트는 URI에서 웹 서버의 IP 주소와 포트번호를 알아내야하는데, 만약 URI에 기술되어 있는 호스트에 방문한적이 최근에 없으면, DNS 이름 분석 인프라를 사용하여 URI에 있는 호스트명을 IP 주소로 변환하는데 시간이 걸린다.

  • TCP 커넥션 : 클라이언트는 TCP 커넥션 요청을 서버에게 보내고 서버가 커넥션 허가 응답을 회신하기를 기다린다.

  • 요청과 트랜잭션 처리 : 커넥션이 맺어지면 HTTP 요청을 새로 생성된 TCP 파이프를 통해 전송하고, 웹 서버는 데이터가 도착하는 대로 TCP 커넥션에서 요청 메시지를 읽어 처리한다.

  • 응답 : 웹 서버가 HTTP 응답을 보낸다.

위의 과정들로 HTTP 트랜잭션들은 지연된다.

TCP 커넥션 핸드셰이크 지연

TCP 커넥션을 할때, 연속으로 IP 패킷을 교환하는데, 이런 패킷 교환은 HTTP 성능을 크게 저하 시킬 수 있다.
아래의 그림은 TCP 커넥션이 핸드셰이크 하는 순서이다.

1. 클라이언트는 새로운 TCP 커넥션을 생성하기 위해 SYN 라는 플래그를 가진 패킷을 보낸다. 커넥션 생성 요청이라는 뜻이다.
2. 서버는 커넥션 요청이 받아들여졌음을 의미하는 SYN+ACK 플래그를 포함한 TCP 패킷을 클라이언트에게 보낸다.
3. 마지막으로 클라이언트는 커넥션이 잘 맺어졌음을 의미하는 서버에게 다시 확인응답으로 ACK를 보낸다. 오늘날의 TCP는 클라이언트가 확인응답 패킷과 함께 데이터를 보낼 수 있다.

결국, HTTP 트랜잭션은 50% 이상의 시간을 TCP를 구성하는데 사용한다.

이후 절에서는 이러한 TCP 구성으로 인한 지연을 제거하기 위해서 HTTP가 이미 존재하는 커넥션을 어떻게 재활용하는지 알아볼 것 이다.

1. 확인응답 지연

각 TCP 세그먼트는 순번과 데이터 무결성 체크섬을 가진다. 각 세그먼트의 수신자는 세그먼트를 온전히 받으면 작은 확인응답 패킷을 송신자에게 반환한다. 만약 송신자가 특정 시간안에 확인응답 메시지를 받지 못하면 패킷이 파기되었거나 오류가 있는 것으로 판단하고 데이터를 다시 전송한다.
확인응답은 크기가 작기 때문에, TCP는 같은 방향으로 송출되는 데이터 패킷에 확인응답을 편승시킨다. 확인응답을 편승시키는 경우를 늘리기 위해서, 많은 TCP 스택은 확인응답 지연은 송출할 확인응답을 특정 시간 동안 버퍼에 저장해두고, 확인응답을 편승시키기 위한 송출 패킷을 찾는다. 그러나 막상 편승할 패킷을 찾으려고 하면 해당 방향으로 송출될 패킷이 많지 않기 때문에, 확인응답 지연 알고리즘으로 인한 지연이 자주 발생한다.

2. TCP 느린 시작

TCP 데이터 전송 속도는 TCP 커넥션이 만들어진 지 얼마나 지났는지에 따라 달라질 수 있다. TCP 커넥션은 시간이 지나면서 자체적으로 튜닝되어서 처음에는 커넥션의 최대 속도를 제한하고 데이터가 성공적으로 전송됨에 따라 속도 제한을 높여나간다. 이렇게 조율하는 것을 TCP 느린시작이라 하고, 이는 인터넷의 급작스러운 부하와 혼잡을 방지하는데 쓰인다.

3. 네이글 알고리즘과 TCP_NODELAY

어떤 크기의 데이터든지 TCP 스택으로 전송할 수 있도록, TCP는 데이터 스트림 인터페이스를 제공한다. 하지만 각 TCP 세그먼트는 40바이트 상당의 플래그와 헤더를 포함하여 전송하기 때문에, TCP가 작은 크기의 데이터를 포함한 많은 수의 패킷을 전송한다면 네트워크 성능은 크게 떨어진다.
네이글 알고리즘은 패킷을 전송하기 전에 많은 양의 TCP 데이터를 한 개의 덩어리로 합친다.
세그먼트가 최대 크기가 되지 않으면 전송을 하지않는다. 다만 다른 모든 패킷들이 확인응답을 받았을 경우에는 최대 크기보다 작은 패킷의 전송을 허락한다.

하지만 네이글 알고리즘은 HTTP 성능과 관련해 여러 문제를 발생시킨다.
1. 크기가 작은 HTTP 메시지는 패킷을 채우지 못하기 때문에 앞으로 생길지 생기지 않을지 모르는 추가적인 데이터를 기다리며 지연된다.
2. 확인응답 지연 알고리즘에서 확인응답을 100~200밀리초 지연시키기 때문에, 네이글 알고리즘과 확인응답 지연을 함께 쓴다면 성능이 좋지 않다.

4. TIME_WAIT의 누적과 포트 고갈

TCP 커넥션의 종단에서 TCP 커넥션을 끊으면, 종단에서는 커넥션의 IP주소와 포트번호를 메모리의 작은 제어영역에 기록해 놓는다. 이는 주소와 포트번호를 사용하는 새로운 TCP 커넥션이 일정 시간 동안에는 생성되지 않게 하기 위한 것이고, 보통 2분 정도 유지된다. 이러한 상태를 TIME_WAIT 상태라고 한다.
서버가 초당 500개 이상의 트랜잭션을 처리할 만큼 빠르다면 TIME_WAIT 포트 고갈이 일어나게 되고, 포트고갈이 일어나게 되면 성능 저하를 발생시킨다. 또한 포트고갈이 일어나지 않더라도 커넥션을 너무 많이 맺거나 대기 상태로 있는 제어블록이 많아지면 극심하게 느려지는 운영체제도 있다.

4.3 커넥션 관리

커넥션 관리가 제대로 이루어지지 않으면 TCP 성능이 매우 안좋아질 수 있다.

예를 들어 3개의 이미지가 있는 웹페이지가 있다고 가정하면, 브라우저가 이 페이지를 보여주려면 네 개의 HTTP 트랜잭션을 만들어야하고, 각 트랜잭션이 새로운 커넥션을 만들어야 한다면, 커넥션을 맺는데 발생하는 지연과 느린 시작 지연이 발생할 것이다.
HTTP 커넥션의 성능을 향상 시킬 수 있는 기술들이 있다. 병렬 커넥션, 지속 커넥션, 파이프라인 커넥션, 다중 커넥션 4가지를 아래 절에서 알아보자.

4.4 병렬 커넥션

앞에서 언급했듯이 브라우저는 HTML, 첫 번째 이미지, 두 번째 이미지 하나씩 내려받는 방식은 너무 느리다. 병렬 커넥션은 네 개의 이미지를 할당받은 각 TCP 커넥션상의 트랜잭션을 통해 병렬로 내려받는다.

위의 그림은 병렬 커넥션 방식을 개략적으로 보여주는데, 일반 커넥션보다 월등히 빠른것을 볼 수 있다. HTML 페이지를 먼저 내려받고 남은 세 개의 트랜잭션이 각각 별도의 커넥션에서 동시에 처리된다.

하지만 병렬 커넥션이 일반적으로 더 빠르기는 하지만, 항상 그렇지는 않다.
네트워크 대역폭이 좁다면 각 객체를 전송받는 것은 느리기 때문에 성능상의 이점은 거의 사라진다. 또한 다수의 커넥션은 메모리를 많이 소모하고 자체적인 성능 문제를 발생시킨다.

브라우저는 실제로 병렬 커넥션을 사용하기는 하지만 대부분 4개의 병렬 커넥션만을 허용한다.

또한, 병렬 커넥션이 페이지를 항상 빠르게 로드하는 것은 아니지만 사용자들은 화면에 여러 개의 객체가 동시에 보이면서 내려받고 있는 상황을 볼 수 있기 때문에 더 빠르다고 생각한다.

4.5 지속 커넥션

처리가 완료된 이후에도 계속 연결된 상태로 있는 TCP 커넥션을 지속 커넥션이라 한다. 해당 서버에 이미 맺어져 있는 지속 커넥션을 재사용함으로써, 커넥션을 맺기 위한 준비작업에 따르는 시간을 절약할 수 있다.

지속 커넥션은 사전 작업과 지연을 줄여주고, 커넥션 수를 줄여줌으로써 병렬 커넥션에 비해 장점을 가지고있다. 하지만 지속 커넥션을 잘못 관리할 경우 계속 연결된 상태로 있는 수많은 커넥션이 쌓이게 될 것이다.
지속 커넥션은 병렬 커넥션과 함께 사용될 때에 가장 효과적이고, 오늘날 많은 웹은 적은 수의 병렬 커넥션만을 맺고 그것을 유지한다.

지속 커넥션 타입에는 HTTP/1.0+에는 'keep-alive' 커넥션이 있고, HTTP/1.1에는 '지속 커넥션'이 있다.

1. HTTP/1.0+의 keep-alive 커넥션

연속적으로 네 개의 커넥션을 생성하여 처리하는 방식과 하나의 지속 커넥션으로만 처리하는 방식을 비교해보았을 때, 시간이 단축됨을 확인할 수 있다.
keep-alive는 HTTP/1.0에서 기본으로 사용되지 않는다. 클라이언트는 keep-alive 커넥션을 사용하기 위햐서는 Connection: Keep-Alive 요청 헤더를 보내야한다.
만약 클라이언트가 Connection: Keep-Alive를 보내지 않으면 서버는 요청을 처리한 후 커넥션을 끊을 것이다.
클라이언트는 Connection: Keep-Alive 응답 헤더가 없다면 서버가 응답한 후에 커넥션을 끊을 것임을 알 수 있다.

2. HTTP/1.1의 지속 커넥션

HTTP/1.1에서는 keep-alive 커넥션을 지원하지 않는 대신, 설계가 더 개선된 지속 커넥션을 지원한다.
HTTP/1.0에서 keep-alive 커넥션과는 달리 HTTP/1.1의 지속 커넥션은 기본적으로 활성화되어있다. HTTP/1.1에서는 Connection: close 헤더를 포함하지 않는 한 모든 커넥션을 지속 커넥션으로 취급한다.
이는 keep-alive 커넥션이 선택사항이 아닐 뿐만 아니라 지원 자체를 하지 않는다는 점에서 이전 HTTP 프로토콜과 다르다.

4.6 파이프라인 커넥션

HTTP/1.1은 지속 커넥션을 통해 요청을 파이프라이닝할 수 있다. 여러 개의 요청은 응답이 도착하기 전까지 큐에 쌓인다. 첫 번째 요청이 네트워크를 통해 서버에 전달되면, 두번째와 세번째 요청이 전달 될 수 있다.

이는 대기시간이 긴 네트워크 상황에서 네트워크상의 왕복으로 인한 시간을 줄여서 성능을 높여준다.

파이프라인 커넥션은 몇가지 제약 사항이 있다.

  • 지속커넥션일때만 파이프라인 커넥션이 가능하다.
  • HTTP 응답은 요청 순서와 같게 와야 한다.

4.7 커넥션 끊기에 대한 미스터리

언제 어떻게 커넥션을 끊는가에 대한 명확한 기준이 없다.
클라이언트가 트랜잭션을 수행중에 전송 커넥션이 끊기게 되면, 클라이언트는 그 트랜잭션이 재시도하더라도 문제가 없다면 커넥션을 다시 맺고 한 번 더 전송을 시도해야 한다.
파이프라인 커넥션에서 조금 더 어려워지는데, 요청 데이터가 전송되었느제 응답이 오기전에 커넥션이 끊기면 클라이언트는 서버에서 얼마만큼의 요청이 처리되었는지 알수 가 없기 때문이다.

한 번 혹은 여러 번 실행됐는지에 상관없이 같은 결과를 반환하는 것을 멱등하다고 하는데, GET, PUT, DELETE등의 메서드들은 멱등하지만, POST와 같이 멱등하지 않는 요청은 파이프라인을 통해 요청하면 안된다. 비멱등인 메서드가 순서에 대해 에이전트가 요청을 다시 보낼 수 있도록 기능을 제공한다고 하더라도, 자동으로 재시도하면 안된다.
예를 들어 대부분의 브라우저는 캐시된 POST 요청 페이지를 다시 로드하려고 할 때, 요청을 다시 보내기를 원하는 대화상자를 보여준다.(가끔,, 새로고침하면 이런 알림창이 뜨던데 이런 이유였구만..!🤔)

profile
강지영입니...🐿️

4개의 댓글

comment-user-thumbnail
2023년 5월 4일

오 이 책 짱좋아

1개의 답글
comment-user-thumbnail
2023년 7월 24일

4장 이해가 안가서 정리본 찾아보다가 발견.... 참고해서 정리할게요 ㅎㅎ

1개의 답글