KKanging

[http 완벽 가이드] 4장 커넥션 관리 본문

cs/http

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

천방지축 개발자 2023. 7. 28. 19:30

이 내용은 http 완변 가이드란 책을 읽고 정리한 내용입니다.

 

더 자세한 내용이 궁금하시면 책을 직접 읽어보시는 걸 추천합니다.

 

4장 커넥션 관리

1. TCP 커넥션

  • HTTP 통신은 TCP/IP 통신을 통해 데이터를 전송한다.
  • 순서
    1. 브라우저가 호스트명을 추출한다.
    2. 브라우저가 포트 번호를 얻는다.
    3. 브라우저가 IP주소와 포트로 TCP 커넥션을 생성한다.
    4. 브라우저가 서버로 HTTP GET 요청 메시지를 보낸다.
    5. 브라우저가 서버에서 온 HTTP 응답 메시지를 읽는다.
    6. 브라우저가 커넥션을 끊는다

1.1 신뢰할 수 있는 데이터 전송 통로인 TCP

  • TCP 커넥션은 인터넷을 안정적으로 연결해준다.
  • 클라이언트에서 데이터를 전송하면 한쪽에 있는 바이트들은 반대쪽으로 순서에 맞게 전달된다.

1.2 TCP스트림은 세그먼트로 나뉘어 IP 패킷을 통해 전송된다.

 

  • TCP는 IP 패킷이라고 불리는 작은 조각을 통해 데이터를 전송한다.
  • HTTP가 메시지를 전송하고자 할 경우 연결되어있는 TCP 커넥션을 통해 데이터를 전송한다.
  • TCP 세그 먼트라는 단위로 데이터 스트림을 잘게 나누고
  • 세그 먼트를 IP 패킷이라고 불리는 봉투에 담아서 인터넷을 통해 데이터를 전달한다.
  • TCP 세그먼트는 하나의 IP 주소에서 다른 IP 주소로 IP 패킷에 담겨 전달 된다.

1.3 TCP 커넥션 유지하기

  • TCP 커넥션을 여러개 가질 수 있다.
  • IP는 컴퓨터에 연결 포트 번호는 애플리케이션에 연결된다.
  • TCP 커넥션은 네 가지 값으로 식별한다.
<발신지 IP 주소, 발신지 포트 , 수신지 IP 주소 ,수신지 포트>

 

  • 두개의 다른 커넥션은 4가지 식별값의 일부는 같을 수 있으나
  • 모두 같을 순 없다.

1.4 TCP 소켓 프로그래밍

  • TCP 프로그래밍을 위해 소켓 API 가 있다.
  • 소켓 API는 TCP커넥션의 생성 연결 입출력 닫기 같은 메서드를 지원한다.

 

2. TCP 의 성능에 대한 고려

  • HTTP 바로 아래 계층이 TCP 계층이기 때문에 TCP의 성능에 영향을 많이 받는다.

2.1 HTTP 트랜잭션 지연

 

  • TCP 커넥션을 연결하는 시간이 트랜잭션이 수행되는 시간보다 많은 시간을 차지하는걸 볼 수 있음
  • HTTP 트랜잭션 시간 소요 부분
    1. 만약 URI에 기술되어있는 호스트에 최근 방문한적 없으면 DNS로 호스트명을 찾는데 시간이 걸림
    2. TCP 커넥션 요청을 서버에게 보내고 서버가 커넥션 허가 응답을 회신하기를 기다린다.
    3. 커넥션이 맺어지면 클라이언트는 요청을 보낸다. 그 요청을 서버에서 처리하는데 시간이 걸리는다.
    4. 서버가 응답을 보내는 것 역시 시간이 걸린다.

2.2 성능 관련 중요 요소

  • TCP관련 지연들
    1. TCP 커넥션의 핸드셰이크 설정
    2. 인터넷의 혼잡을 제어하기 위한 TCP의 느린 시작
    3. 데이터를 한데 모아 한 번에 전송하기 위한 네이글 알고리즘
    4. TCP 의 편승 확인응답을 위한 확인응답 지연 알고리즘
    5. TIME_WAIT 지연과 포트 고갈

2.3 TCP 커넥션의 핸드셰이크 지연

  • TCP 커넥션을 맺기 위한 조건을 갖추기 위해 연속으로 IP패킷 교환을 한다.
  • 이런 패킷 교환은 HTTP 성능을 크게 저하 시킬 수 있다.
  • 다음은 TCP 커넥션이 핸드 셰이크를 하는 순서이다.
  • 아주 큰 데이터를 주고 받는 경우가 아니라면 이 작은 데이터로 하는 커넥션 핸드 셰이크는 큰 지연을 유발한다.

2.4 확인응답 지연

  • 수신자는 세그먼트를 온전히 받으면 확인 응답 패킷을 송신자에게 반환한다.
  • 만약 송신자가 특정 시간내에 확인응답 메시지를 받지 못하면 패킷이 파기 되었거나 오류가 있는걸로 판단하여 다시 데이터를 전송한다.
  • 확인 응답은 데이터의 크기가 작기 때문에 TCP는 같은 방향으로 송출되는 데이터 패킷에 확인 응답을 같이 편승 시킨다(PIGGYBACK) →이는 효율적이다.
  • 좀 더 편승을 높이기 위해 확인 응답 지연 알고리즘을 이용한다.
  • → 확인 응답을 바로 보내지 않고 버퍼에 0.1~0.2초 동안 보관하여 같이 보낼 메시지를 찾는다. 못찾으면 따로 보낸다.
  • 하지만 이는 요청과 응답만 있는 HTTP 통신에서는 편승의 기회가 잘 없기에 확인 응답 지연은 성능에 악영향을 끼친다.
    • 확인 응답 지연은 비활성화가 가능하다.

2.5 TCP의 느린 시작으로 인한 지연

  • TCP의 데이터 전송 속도는 TCP 커넥션이 만들어진 지 얼마나 지났는지에 따라 달라질 수 있다.
  • TCP 커넥션은 시간이 지날 수록 튜닝된다.
  • 처음에는 커넥션의 최대 속도를 제한하고 데이터가 성공적으로 전송됨에 따라서 속도 제한을 높여나간다. → 이런 느린 시작을 TCP 느린 시작이라고 부른다.
  • 이는 새로 연결하는 TCP는 이미 튜닝된 커넥션보다 느리다는 점이 있다. 아래에서 지속 커넥션에 대해 배운다.

2.6 네이글(Nagle) 알고리즘으로 인한 지연

  • TCP 세그먼트는 40바이트 상당의 플래그와 헤더를 포함하여 전송하기 때문에, 아주 작은 크기의 데이터를 전송하면 성능이 떨어진다.
  • → 이를 해결한게 네이글 알고리즘이다.
  • 네이글 알고리즘은 세그먼트가 최대 크기가 되지 않으면 보내지 않고 버퍼에 넣는다.
  • 그리고 다른 패킷이 모두 확인 응답을 받거나, 충분한 크기가 되면 보낸다.
  • 이는 성능 관련 큰 문제를 낳는다.
  1. 작은 크기의 메시지는 언제 채워질 지 모르기에 하염없이 기다려야한다.
  2. 확인 응답 지연 알고리즘과 같이 쓰이면 엄청 비효율적이다.
  3. → 확인 응답이 다 오면 보내지는데 확인 응답은 또 몇초 딜레이를 가지기 때문이다.
  • 네이글 알고리즘 또한 비활성화가 가능하다.
  • → 비활성화 시 작은 크기의 패킷이 너무 생기지 않도록 조심해야한다.

2.7 TIME_WAIT의 누적과 포트 고갈

  • 이는 실제 성능에는 문제가 되지 않지만 측정시 문제가 되므로 측정하는 사람의 혼란을 줄 수 있다.
  • TCP 커넥션을 끊으면, 종단에서는 커넥션의 IP 주소와 포트 번호를 메모리의 작은 제어영역(control block)에 기록해둔다. (커넥션 종료 지연)
  • 이 정보는 같은 주소와 포트 번호를 사용하는 새로운 TCP 커넥션이 일정 시간 동안 생성되지 않게 하기 위한 것으로, 이 시간을 TIME_WAIT라고 한다.

(보통 세그먼트의 최대 생명주기의 두 배 정도로 설정되며 2분 정도 : 2MSL)

  • TIME_WAIT 사용 이유 1 : 지연 패킷이 새로운 커넥션에 끼어드는 것을 막을 수 있다

 

TIME_WAIT을 사용하면 이전 커넥션의 지연 패킷이 새로운 커넥션에 삽입되는 문제를 방지할 수 있다.

매우 드문 경우이긴 하지만, SEQ=3의 패킷이 지연된 후 새로운 커넥션이 생성되었고, 때마침 해당 커넥션에 SEQ=3이 들어올 타이밍이었다면 데이터 무결성 문제가 발생할 수 있다.

TIME_WAIT 사용 이유 2 : LAST_ACK가 유실된 경우를 막을 수 있다

 

위 사진처럼 마지막 ACK가 유실된 경우, 상대는 LAST_ACK 상태에 빠지고, 새로운 커넥션이 SYN 패킷 전달시 RST를 리턴한다.

따라서 반드시 TIME_WAIT이 일정 시간 남아있어서 패킷의 오동작을 막아야 한다.

  • 이런 커넥션 종료 지연은 평상시에는 큰 문제가 되지 않지만, 부하 테스트 상황에서는 문제가 될 수도 있어서 주의가 필요하다.

3. HTTP 커넥션 관리

  • 이 장은 커넥션의 생성과 최적화에 관한 내용이다.

3.1 흔히 잘못 이해하는 Connection 헤더

  • 클라이언트와 서버 사이에 HTTP는 프락시 서버 , 캐시 서버 등과 같은 중개 서버가 놓이는 것을 허락한다.
  • 어떤 경우에는 두 개의 인접한 HTTP 애플리캐이션이 현재 맺고 있는 커넥션에만 적용될 옵션을 지정해야 할 때가 있다.
  • HTTP 커넥션 헤더 필드는 커넥션 토큰을 쉼표로 구분하여 가지고 있다. 그 값들은 다른 커넥션에 전달되지 않는다.
  • connection 토큰 값은 아래 3가지 종류로 올 수 있다.
    1. HTTP 헤더 필드 명 (홉별 헤더 명) : 여기 적힌 헤더들은 모두 다른 곳으로 전달하는 시점에는 삭제되어야 한다예외적으로 여기에 적혀 있지 않아도 홉별(hop-by-hop) 헤더인 것들도 있다 : Proxy-Authenticate, Proxy-Connection, Transfer-Encoding, Upgrade
    2. 임시적인 토큰 값 : 커넥션에 대한 비표준 옵션을 의미
    3. close 값 : 이 트랜잭션이 끝나면 커넥션이 종료되어야 함을 의미
  • 홉별(hop-by-hop): 홉은 각 서버를 의미 , 홉별은 특정 두 서버 간에만 영향을 미치고 다른 서버 간에는 영향을 미치지 않음을 뜻한다.
  • Untitled
  • HTTP 헤더명에 적힌 헤더는 다른 커넥션에 전달 되면 안된다.

3.2 순차적인 트랜잭션 처리에 의한 지연

  • 순차적인 트랜잭션을 처리한다고 가정하고 이미지 4개를 요청한다고 가정해보자
  • TCP 커넥션1을 맺고 리소스를 트랜잭션1 커넥션2을 맺고 트랜잭션 2…
  • 물리적인 지연 뿐 아니라 사용자는 이미지가 하나씩 로드 되므로 심리적인 지연도 크다.
  • 이를 해결할 기술 4가지가 있다.
  1. 병렬(parallel) 커넥션
  2. 지속(persistent) 커넥션
  3. 파이프라인(pipelined) 커넥션
  4. 다중(multiplexed) 커넥션

4. 병렬 커넥션

  • 위 순차처리는 4가지 이미지를 로드하는데 한개의 트랜잭션이 끝날 때까지 대기해야 했다.
  • 병렬 커넥션은 4개의 커넥션을 거의 동시에 연결하여(약간의 지연만 발생) 트랜잭션을 병렬 처리할 수 있다.

병렬 커넥션이 항상 더 빠르지는 않다.

  • 클라이언트의 대역폭이 좁을 때에는 대부분에 시간을 데이터를 전송하는 데만 쓸 것이다.
    • 여러 개의 객체를 병렬로 내려받는 경우, 이 제한된 대역폭 내에서 각 객체를 전송 받는것은 느리기 때문에 성능상의 장점은 거의 없어진다.
  • 병렬 커넥션이 빠르다고 많이 연결하게 되면 서버에 부하가 크다.
    • 그래서 브라우저는 병렬 커넥션 수를 제한한다.
  • 그리고 서버는 과도한 커넥션이 연결되면 그것을 끊어버릴 수도 있다.

5. 지속 커넥션

  • 보통 클라이언트는 같은 사이트에 여러 개의 커넥션을 맺는다.
    • 예를 들어 이미지 같은 웹사이트도 있고, 상당 수의 하이퍼링크가 같은 사이트를 가리킨다.(사이트 지역성 site locality)라고 부름
  • 이때 웹페이지를 가지고 온다고 서버와 커넥션을 맺고 이미지를 가지고 온다고 커넥션을 맺으면
  • TCP 커넥션의 지연 때문에 성능에 엄청 비효율적이다.
  • 한번 맺은 커넥션을 재사용 함으로써 TCP의 느린 시작을 피할 수 있다. 이를 지속 커넥션이라 부른다.

5.1 지속 커넥션 VS 병렬 커넥션

  • 병렬 커넥션은 순차 커넥션 보다 빠르다는 장점이 있지만 TCP의 느린 시작 그리고 실제로 연결할 수 있는 병렬 커넥션의 수에는 제한이 있다는 점이란 단점들이 존재한다.
  • 이에 반해 지속 커넥션에 장점이 있다.
    • TCP커넥션에 느린시작을 없에줌
    • 튜닝된 커넥션을 유지
    • 커넥션 수를 줄여줌
  • 하지만 이를 관리하지 못할 경우 수많은 계속 연결 상태로 있는 커넥션이 쌓일 것이다.
    • 이는 서버나 클라이언트에 불필요한 소모를 발생한다.
  • 지속 커넥션은 병렬 커넥션과 같이 사용할 때 효과적이다.
    • 많은 수의 애플리케이션은 적은 수의 병렬 커넥션만을 맺고 그것을 유지한다.
    • 두가지 지속 커넥션 타입이 있는데
      1. HTTP/1.0+에 keep-alive 커넥션이 있고
      2. HTTP/1.1 에는 지속 커넥션이 있다.

5.2,3,4 HTTP/1.0+ 의 Keep-Alive 커넥션 동작과 옵션

  • Keep-Alive은 HTTP 1.1 명세에는 빠졌지만 아직 HTTP/1.0+를 사용하는 애플리캐이션이 많기 때문에 이를 처리할 수 있어야한다.
Connection: Keep-Alive
  • 위 헤더가 있는 요청을 클라이언트가 보내면 클라이언트가 이 트랜잭션이 끝나도 커넥션을 유지하기를 원한다는 의미이다.
  • 응답에 위 헤더를 포함시켜야 클라이언트는 이 커넥션이 지속되는지 알 수 있다.
    • 만약 포함시키지 않을 경우 클라이언트는 커넥션이 서버가 끊을 것이라고 추정한다.
  • Keep-Alive 헤더는 커넥션을 유지하기 바라는 요청일 뿐이다. 클라이언트나 서버가 이를 받았다고 해서 무조건 그것을 따를 필요는 없다.
  • 언제든지 Keep-Alive 헤더를 끊을 수도 제한할 수도 있다.
  • timeout 파라미터: 커넥션이 얼마간 유지될건지.
  • max 파라미터: 커넥션이 몇 개의 HTTP 트랜잭션을 처리할 때까지 유지될 건지.
    • 하지만 이대로 동작할지는 보장하지 않는다.
  • 디버깅을 주목적으로 하는 임의의 속성. 이름=값 형식

 

5.5 Keep-Alive 커넥션 제한과 규칙

  • Keep-Alive sms HTTP/1.0에서 기본으로 사용되지는 않는다. → Keep-Alive 커넥션을 맺고자한다면 Connection : Keep-Alive 헤더를 포함하고 보내야한다.
  • 클라이언트는 Connection : Keep-Alive 헤더가 없는 것을 보고 요청 처리 후 커넥션을 끊을거라 예상한다.
  • 커넥션이 끊어지기 전 엔터티 본문에 길이나 타입을 정확하게 알아야 커넥션을 유지할 수 있다.
    • 이는 앤터티에 본문에 대한 인코딩이 잘되어야한다. 트랜잭션이 끝나는 시점에 기존 메시지의 끝과 새로운 메시지의 시작을 정확히 알 수 없기 때문
  • 프록시와 게이트웨이는 메시지를 전달하거나 캐시에 넣기 전에 Connection 헤더에 명시된 모든 헤더 필드와 Connection 헤더를 제거해야 한다.
  • keep-alive 커넥션은 Connection 헤더를 인식하지 못하는 프록시 서버와는 맺어지면 안된다(Dumb Proxy로 인해 헤더가 다음 홉으로 넘어가기 떄문에. 그러나 그런 경우가 많이 발생한다)
  • HTTP/1.0을 따르는 기기로부터 받은 모든 Connection 헤더 필드는 무시해야한다.
  • 클라이언트는 응답 전체를 모두 받기 전에 커넥션이 끊어진 경우, 요청을 다시 보낼 수 있게 준비되어 있어야 한다.

5.6 Keep-Alive와 멍청한 프록시 문제

Untitled

  • 멍청한 프록시는 connetion헤더를 인식하지 못하고 그냥 메시지를 보낸다.
  • 이에 클라이언트와 서버는 keep-alive로 인식하여 커넥션이 유지될것이라 믿는다.
  • 하지만 프록시는 연결이 끝날때까지 대기할 것이고, 클라이언트가 지속될거라고 믿는 커넥션에 요청을 보내면 프록시는 대기중이라 이를 해결하지 않는 문제가 발생한다.

5.7 Proxy-Connection 살펴보기

  • 모든 웹 애플리케이션이 HTTP 최신 버전을 지원하지 않아도 멍청한 프록시 문제를 해결할 수 있는 차선책으로 제시된 게 Proxy-Connection 헤더이다.
  • 이걸 이용하면, 멍청한 프록시가 Proxy-Connection 헤더를 전달하더라도, 웹 서버는 이걸 무시하기 때문에 별문제가 되지 않는다.
  • 영리한 프록시(지속 커넥션을 이해하는)라면, 의미 없는 Proxy-Connection 헤더를 Connection 헤더로 바꿈으로써 원하던 효과를 얻을 수 있다.
  • 이 방식은 서버와 클라이언트 사이에 프록시가 단 한개 존재하는 경우에만 동작한다.
  • 프록시가 2개 이상이고, 그 사이에 멍청한 프록시가 섞여있는경우 영리한 프록시가 이를 connection 헤더로 변환하면 똑같이 문제가 발생한다.

5.8 HTTP/1.1의 지속 커넥션

  • HTTP/1.1은 keep-alive 커넥션을 지원하지 않는 대신, 설계가 개선된 지속 커넥션을 지원한다.
    • 더 잘 동작한다.
  • keep-alive 커넥션과 지속 커넥션은 기본으로 활성화 되어있다.
  • 커넥션을 끊기를 원한다면 connection:close를 명시해야한다.
  • connection:close이 없으면 응답 후에도 커넥션이 계속 유지되는것으로 간주한다.
    • 하지만 이는 언제든지 클라이언트나 서버에서 끊을 수 있으며 connection:close가 상태를 항상 나타내지는 않는다.

5.9 지속 커넥션의 제한과 규칙

  • keep-alive와 마찬가지로 엔티티 본문은 정확한 Content-Length를 가지거나 청크 전송 인코딩되어 있어야 한다.
  • HTTP/1.1 프록시는 클라이언트와 서버 각각에 대해 별도의 지속 커넥션을 맺고 관리해야 한다.
  • 단, 프록시 서버는 클라이언트의 커넥션 관련 지원 범위를 알고 있지 않은 한 지속 커넥션을 맺으면 안된다
    • 오래된 프록시가 Conenction 헤더를 전달하는 문제가 생길 수 있기 때문에
  • HTTP/1.1 애플리케이션은 중간에 끊어지는 커넥션을 복구할 수 있어야 한다. 클라이언트는 다시 보내도 문제가 없는 요청이라면 가능한 한 다시 보내야 한다.
  • 하나의 사용자 클라이언트는 서버의 과부하를 방지하기 위해서 , 넉넉잡아 두 개의 지속 커넥션을 유지해야 한다.
  •  

6 파이프라인 커넥션

  • 여러 개의 요청은 응답이 도착하기 까지 Queue에 쌓인다.
  • 이는 첫번째 요청을 보내면서 2번째 3번째 요청을 보낼 수 있다. 이는 왕복시간을 줄여서 시간을 단축시켜준다.
  • HTTP/1.1은 지속 커넥션을 통해 요청을 파이프라이닝 할 수 있다. (하나의 TCP 커넥션을 통한 병렬 HTTP 요청이 가능)

그러나 파이프라인에는 여러가지 제약사항이 있다.

  • HTTP 클라이언트는 커넥션이 지속 커넥션인지 확인하기 전까지는 파이프라인을 이어서는 안된다.
  • HTTP 응답은 요청 순서와 같게 와야 한다. 이걸 순서에 맞게 정렬시킬 방법은 없다.
  • HTTP 클라이언트는 언제 연결이 끊어지더라도 완료되지 않은 요청이 파이프라인에 있으면 언제든 다시 요청을 보낼 준비가 되어 있어야 한다.
  • POST 요청과 같이 반복해서 보낼 경우 문제가 생기는 요청(비멱등한 요청)은 파이프라인을 통해 보내면 안된다.
    • 에러가 발생하면 파이프라인을 통한 요청 중 어떤 것이 서버에서 처리되었는지 클라이언트는 알 수 없고, POST 같은 비멱등 요청을 재차 보내면 문제가 생길 수 있기 때문이다.

https://velog.velcdn.com/images/bahar-j/post/a9c4c003-2f4f-43f6-ac15-36ead2a8e432/image.png

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

  • 커넥션 관리(특히 언제 어떻게 커넥션을 끊는가)에는 명확한 기준이 없다.
  • 이 이슈는 수많은 개발자가 알고 있는 것보다 더 미묘하며, 그에 관한 기술 문서도 별로 없다.

7.1 마음대로' 커넥션 끊기

  • HTTP 클라이언트, 서버, 혹은 프락시는 언제든지 TCP 전송 커넥션을 끊을 수 있다.
  • 보통 커넥션은 메시지를 다 보낸 다음 끊지만, 에러가 있는 상황에서는 헤더의 중간이나 다른 엉뚱한 곳에서 끊길 수 있다.
  • 지속 커넥션이 일정 시간 동안 요청을 전송하지 않고 유휴 상태에 있으면 서버는 그 커넥션을 끊을 수 있다.
  • 하지만 서버가 유휴 상태에 있는 커넥션을 끊는 시점에, 서버는 클라이언트가 다시 요청하지 않을 것이라고 확신하지 못한다. 클라이언트가 다음 요청을 보낸다면 문제가 생긴다.

7.2 Content-Length와 Truncation

  • 각 HTTP 응답은 본문의 정확한 크기 값을 가지는 Content-Length 헤더를 가지고 있어야 한다.
  • 클라이언트나 프락시가 커넥션이 끊어졌다는 HTTP 응답을 받은 후, 실제 전달 된 엔터티 본문의 길이와 Content-Length가 일치하지 않거나 Content-Length 헤더가 존재하지 않는다면 수신자는 데이터의 정확한 길이를 서버에게 물어봐야 한다.
  • 만약 수신자가 캐시 프락시일 경우 응답을 캐시하면 안되며, Content-Length를 정정하려 하지 말고 메시지를 받은 그대로 전달해야 한다.

7.3 커넥션 끊기의 허용, 재시도, 멱등성

  • 커넥션은 에러가 없더라도 언제든 끊을 수 있다.
  • HTTP 애플리케이션은 예상치 못하게 커넥션이 끊어졌을 경우를 대응할 수 있도록 준비해야 한다.
  • 클라이언트는 트랜잭션 수행 중 커넥션이 끊어졌을 때 다시 트랜잭션을 전송해도 문제가 없다면 커넥션은 다시 맺고 한번 더 전송해야 한다.
  • GET 요청은 반복적으로 요청하더라도 결과적으로 아무런 영향을 끼치지 않지만, 온라인 서점에서 주문을 하는 POST 요청은 반복할 경우 여러 번 주문이 될 것이기 때문에 반복은 피해야 한다.
  • 한 번 혹은 여러 번 실행해도 같은 결과를 반환한다면 그 트랜잭션은 멱등(idempotent)하다고 한다.
  • GET, HEAD, PUT, DELETE, TRACE, OPTIONS 메서드들은 멱등
  • POST와 같은 비멱등 메서드는 파이프라인을 통해 요청하면 안된다.
  • 비멱등 요청을 다시 보내야 한다면, 이전 요청에 대한 응답을 받을 때까지 기다려야 한다.
  • 비멱등 메서드나 순서에 대해 에이전트가 요청을 다시 보낼 수 있도록 기능을 제공할 때 자동으로 재시도하면 안된다. 캐시된 POST 요청 페이지를 다시 로드하려고 할 때, 요청을 다시 보내기를 원하는지 묻는 대화상자를 보여준다.

7.4 우아한 커넥션 끊기

  • TCP 커넥션은 양방향이며 양쪽에는 데이터를 읽거나 쓰기 위한 입력 큐와 출력 큐가 있다. 한쪽 출력 큐에 있는 데이터는 다른 쪽의 입력 큐에 보내질 것이다.

전체 끊기와 절반 끊기

  • 애플리케이션은 TCP 입력 채널과 출력 채널 중 한 개만 끊거나 둘 다 끊을 수 있다.
  • close()를 호출하면 TCP 커넥션의 입력 채널과 출력 채널의 커넥션을 모두 끊는다.
  • shutdown()을 호출하면 입력 채널이나 출력 채널 중 하나를 개별적으로 끊을 수 있다.

TCP 끊기와 리셋 에러

  • 단순한 HTTP 애플리케이션은 전체 끊기만을 사용할 수 있다.
  • 하지만 애플리케이션은 다른 애플리케이션들과 통신할 때, 그리고 그들과 파이프라인 지속 커넥션을 사용할 때, 기기들에 예상치 못한 쓰기 에러를 발생하는 것을 예방하기 위해 '절반 끊기'를 사용해야 한다.
  • 보통은 커넥션의 출력 채널을 끊는 것이 안전하다.
  • 클라이언트에서 이미 끊긴 입력 채널에 데이터를 전송하면, 서버의 운영체제는 TCP 'connection reset by peer' 메시지를 클라이언트에게 보낸다.
  • 대부분 운영체제는 이것을 심각한 에러로 취급하여 버퍼에 저장된, 아직 읽히지 않은 데이터를 모두 삭제한다.

우아하게 커넥션 끊기

  • 우아한 커넥션 끊기를 구현하는 것은 애플리케이션 자신의 출력 채널을 먼저 끊고 다른 쪽에 있는 기기의 출력 채널을 끊기는 것을 기다리는 것이다.