HTTP 커넥션 관리

jy.YOON·2023년 3월 16일
0

HTTP

목록 보기
2/3

HTTP 커넥션 관리

post-thumbnail

본 글은 HTTP 완벽 가이드를 읽고 정리한 글입니다.








1.Connection 헤더

여러 서버들을 하나하나 거치면서 어느 파이프라인은 커넥션을 유지하며, 어느 파이프라인은 끊어줘야 하는 여부를 Connection 헤더에 대한 정보로 판단한다.

커넥션 토큰의 HTTP 헤더 필드 들은 현재 커넥션만을 위한 정보임으로 다음 커넥션에 전달되면 안된다.

Connection 헤더홉별(hop-by-hop)헤더명들을 기술하는데 현재 커넥션의 헤더정보가 다음 커넥션에 전달되지 않도록 방지하는 역할을 한다.

 *홉별(특정 두 서버간에만 영향을 미치고 다른 서버간에는 영향을 미치지 않음을 뜻함)

HTTP headers | Connection - GeeksforGeeks
[Connection 헤더]




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

각 트랜잭션이 새로운 커넥션을 필요로 하면, 커넥션을 맺는 과정에서 발생하는 지연과, 느린 시작 지연이 발생한다.

  • 순차적 로드 방식의 단점
    • 특정 브라우저의 객체를 화면으로 배치할때, 객체의 크기를 알아야 하기 때문에 모든 객체를 내려받기 전까지 텅빈 화면을 보여준다(해당 경우는 객체들을 연속해서 하나씩 내려받는게 효율적)
    • 사용자는 흰색의 텅빈 화면만 보기 때문에 불편한 경험을 함
  • HTTP 커넥션 성능 향상 방안
    • 병렬커넥션
    • 지속커넥션
    • 파이프라인 커넥션
    • 다중 커넥션


3. 병렬 커넥션

커넥션 관리 - TCP 커넥션의 종류 | 필오의 개발일지

클라이언트가 여러개의 커넥션을 맺음으로써 여러 개의 HTTP 트랜잭션을 병렬로 처리할 수 있게 한다.

  • 장점
    • 한 개의 커넥션이 대역폭을 모두 사용하는것이 아니라면, 남은 대역폭을 사용함으로 써 페이지를 더 빠르게 내려받을 수 있다.
    • 각 커넥션의 지연시간을 겹치게 하면 총 지연 시간을 줄일 수 있다.
    • 사용자 입장에서는 여러 작업이 일어나는 것을 눈으로 확인할 수 있기 때문에 더 빠르게 느껴질 수 있다.
  • 단점
    • 클라이언트의 네트워크 대역폭이 좁을땐, 병렬 커넥션의 성능상의 장점은 거의 없어진다.
    • 다수의 커넥션은 메모리를 많이 소모하고 자체적인 성능 문제를 발생시킨다.
    • 각각의 새로운 커넥션은 TCP 느린 시작으로 인한 성능 저하
    • 실제 연결할 수 있는 병렬 커넥션의 수에는 제한이 있음(브라우저 종속)

  • TMI
    • 최근 브라우저들은 6~8개의 병렬커넥션을 지원한다.



4. 지속 커넥션

HTTP/1.1을 지원하는 기기는 처리가 완료된 후에도 TCP 커넥션을 유지하며 앞으로 있을 HTTP 요청에 재사용할 수 있다. 처리가 완료된 후에도 계속 연결된 상태로 있는 TCP 커넥션을 지속 커넥션이라고 한다.

클라이언트나 서버가 커넥션을 끊기 전까진 트랜잭션 간에도 커넥션을 유지한다.

  • 장점
    • 커넥션을 맺는데 소모되는 시간을 절약할 수 있다.
    • TCP 느린시작으로 인한 지연을 피함으로써 더 빠르게 데이터를 전송할 수 있다.(튜닝된 커넥션)
    • 커넥션의 수 감소
  • 단점
    • 잘못 관리할 경우 불필요한 소모 발생
  • TMI
    • 병렬 커넥션과 함께 사용될때 가장 효과적이라고 한다.



5. HTTP/1.0+의 Keep-Alive 커넥션

HTTP/1.1 명세에서 부터는 제외되었다.

  • 동작

HTTP/1.0 에서 클라이언트는 Connection:Keep-Alive 헤더를 요청에 포함시켜 보내고 서버로 부터의 응답에

Connection:Keep-Alive 헤더가 없으면 클라이언트는 서버가 Keep-Alive를 지원하지 않고, 응답 메시지가 전송된뒤 서버 커넥션을 끊을 것이라 추정한다.

  • 옵션

    클라이언트나 서버는 Keep-Alive 요청을 받았다고 해서 무조건 그것을 따를 필요는 없음

    • timeout

      커넥션이 얼마나 유지될것인지 의미, 그러나 이대로 동작한다는 보장은 없음

    • max

      커넥션이 몇개의 HTTP 트랜잭션을 처리할 때까지 유지될것인지 의미, 그러나 이대로 동작한다는 보장은 없음



6. Keep-Alive 커넥션 제한과 규칙

​ keep-alive는 HTTP/1.0에서 기본으로 사용되지 않는다.

  • 커넥션을 계속 유지하려면 모든 메시지에 Connection:Keep-Alive 헤더를 포함해 보내야 한다.

    • 클라이언트가 Connection:Keep-Alive 헤더를 보내지 않으면 서버는 요청 처리후 커넥션을 끊는다.
  • 클라이언트는 응답에 Connection:Keep-Alive 헤더가 없는 걸 확인하여 응답 후 서버가 커넥션을 끊을것임을 알 수 있다.

  • 엔티티 본문의 길이를 알 수 있어야 커넥션을 유지할 수 있다.

    • 트랜잭션이 끝나는 시점에 기존 메시지의 끝과 새로운 메시지의 시작점을 정확히 알 수 있어야 하기 때문이다.
  • 프락시/게이트웨이 는 Connection 헤더 규칙을 철저히 지켜야 한다.(추후 복잡한 flow에서 터지는 Connection 문제)

    • 프락시/게이트웨이는 메시지 전달 또는 캐시에 넣기 전에 Connection 헤더에 명시된 모든 헤더 필드와 Connection 헤더를 제거해야한다.
  • Connection 헤더를 인식하지 못하는 프락시 서버와는 맺어지면 안된다.(멍청한 프락시 예방. 그러나 현실적으로 힘듬)

  • HTTP/1.0을 따르는 기기로부터 받는 모든 Connection 헤더는 무시

    • 오래된 프락시 서버로 부터 실수로 전달될 수있는 정보이기 때문
  • 클라이언트는 응답 전체를 받기전에 커넥션이 끊어졌을 경우, 요청을 다시 보낼 수 있게 준비되어 있어야 한다



7. Keep-Alive와 멍청한 프락시

Connection 헤더가 관련없는 다음 프락시로 전달되어 생기는 문제점

7-1. Connection 헤더의 무조건 전달

프락시는Connection 헤더를 이해하지 못해서 해당 헤더들을 삭제하지 않고 요청 그대로를 다음 프락시에게 전달

멍청한 프록시

(그림출처:https://beomy.github.io/tech/etc/http-version/ )

위 문제로 인하여 해당 요청은 프락시로 부터 무시되고 브라우저는 아무런 응답 없이 로드중이라는 표시만 나옴

브라우저는 자신이나 서버가 타임아웃이 나서 커넥션이 끊길때 까지 기다린다.

7-2. 프락시와 홉별 헤더

위의 잘못된 통신을 피하려면 프락시는 Connection 헤더Connection 헤더에 명시된 헤더들을 절대 전달하면 안된다.

Keep-Alive, Proxy-Authenticate, Proxy-Connection, Transfer-Encoding, Upgrade와 같은 홉별헤더들도 전달하면 안된다.


8. Proxy-Connection

모든 웹 애플리케이션이 HTTP 최신 버전을 지원하지 않아도 모든 헤더를 무조건 전달하는 문제를 해결할 수 있는 기발한 차선책 Proxy-Connection 헤더 사용하기(모든 상황에서 동작하지 않음)

  • 홉별 헤더들은 한 개의 특정 커넥션에서 쓰이고 그 이후에는 전달하면 안 된다.
    • 홉별 헤더를 전달받은 서버가 그 헤더를 자신과 프락시 간의 커넥션에 대한것으로 오해하면서 문제가 발생하는 것이다.
  • 넷스케이프 에서는 이 문제를 해결하고자 비표준인 Proxy-Connection 확장 헤더를 프락시에게 전달
  • 웹서버는 Proxy-Connection 헤더를 무시하지만 영리한 프락시(지속 커넥션 핸드쉐이킹)이면 의미없는 Proxy-Connection 헤더를 Connection 헤더로 바꿈으로서 원하는 효과를 얻는다.

img

위 방식은 클라이언트와 서버 사이에 한 개의 프락시만 있는 경우에서만 동작한다.

그러나 주변에 멍청한 프락시가 있다면 잘못된 헤더를 만들어내는 문제가 다시 발생

HTTP Keep Alive 알아보기



9. HTTP/1.1의 지속커넥션

HTTP/1.1에서는 keep-alive 커넥션을 지원하지 않는 대신 설계가 더 개선된 지속커넥션을 지원한다.

  • HTTP/1.1의 keep-alive 지속 커넥션은 기본으로 활성화 되어있다.

  • HTTP/1.1에서 트랜잭션이 끝난 뒤 다음 커넥션을 끊을려면 Connection: close 헤더를 명시해야한다.

    • HTTP/1.1 클라이언트는 응답에 Connection: close 헤더가 없으면 응답후에도 커넥션을 계속 유지하는것으로 추정한다.

    • 그러나 서버든 클라이언트든 커넥션을 언제든 끊을 수 있기때문에 Connection: close를 보내지 않는것이 서버가 커넥션을 영원히 유지하겠다는 의미는 아니다.



10. 지속커넥션의 제한과 규칙

  • 클라이언트가 요청에 Connection: close 헤더를 보냈으면 클라이언트는 그 커넥션으로 추가적인 요청을 보낼 수 없다.
  • 클라이언트가 해당 커넥션에 추가적인 요청을 보내지 않으면 마지막 요청에 Connection: close 헤더를 보내야한다.
  • 모든 메시지가 자신의 길이 정보를 정확히 갖고있을때만 커넥션을 지속할 수 있다.
    • 정확한 Content-Length 값을 가지거나 청크 전송 인코등으로 인코드 되어있어야 한다.
  • HTTP/1.1 프락시는 클라이언트와 서버 각각에 대해 별도로 지속 커넥션을 맺고 있어야한다.
  • HTTP/1.1 프락시는 커넥션에 관해서 클라이언트의 지원 범위를 알고 있지 않는한 지속 커넥션을 맺으면 안된다.(현실적으로 쉽지않음, 많은 벤더가 이 규칙을 지키지 않음)
    • 오래된 프락시가 Connection 헤더를 전달하는 문제가 발생할 수 있음
  • HTTP/1.1은 Connection 헤더와 상관없이 언제든 커넥션을 끊을 수 있다.
  • HTTP/1.1 애플리케이션은 중간에 끊어지는 커넥션을 복구할 수 있어야한다.
    • 커넥션이 끊기는 경우 클라이언트는 요청을 다시 보낼 준비가 되어 있어야 한다.
  • 서버 과부하 방지를 위해 하나의 사용자 클라이언트는 넉넉잡아 2개의 지속 커넥션만을 유지해야한다.



11. 파이프라인 커넥션

지속 커넥션을 통해 요청을 파이프라이닝 할 수 있으며, 이는 keep-alive 커넥션의 성능을 높여준다.

img

11-1. 파이프라인의 제약사항

  • HTTP 클라이언트는 커넥션이 지속 커넥션인지 확인하기 전까지는 파이프라인을 이어서는 안된다.
  • HTTP 응답은 요청 순서와 같게 와야한다.
    • HTTP 메시지는 순번이 없기때문에 응답이 순서없이 오면 정렬시킬 방법이없음
  • HTTP 클라이언트는 커넥션이 끊기거나, 완료되지 않은 요청이 파이프라인에 있으면 다시 요청을 보낼 준비가 되어있어야한다.
    • 클라이언트가 10개의 요청을 보내더라도 서버는 5개의 요청만 처리하고 커넥션을 끊을 수도 있다.
  • 비멱등 요청을 반복해서 파이프라인에 보내면 안된다.
    • 비멱등 요청을 여러번 보내는 경우, 에러 발생시 파이프라인을 통한 요청중에서 어떤 것들이 서버에 처리되었는지 알 방법이 없다.



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

언제 어떻게 커넥션을 끊는가에 대한 명확한 기준이 없다.

12-1. 마음대로 커넥션 끊기

​ 클라이언트,서버,프록시는 언제든지 TCP 전송 커넥션을 끊을 수 있다.

  • 지속커넥션이 일정 시간동안 요청을 전송하지 않고 유휴상태에 있으면 서버는 그 커넥션을 끊을 수 있다.
    • 서버가 커넥션을 끊는 시점에 서버는 클라이언트가 데이터를 전송하지 않을 것이라고 확신할 수 없다 --> 클라이언트는 요청 메시지를 보내는 도중 문제발생

12-2. Content-length 와 Truncation

HTTP 응답은 본문의 정확한 크기 값을 가지는 Content-length 헤더를 가지고 있어야 한다.

  • 일부 오래된 HTTP 서버는 자신이 커넥션 종료시 Content-length 헤더를 생략하거나 잘못된 길이로 응답하는 경우가 있음
  • 클라이언트나 프락시는 커넥션이 끊겼다는 HTTP 응답을 받은 후 실제 전달한 엔티티 길이와 Content-Length의 값이 일치 하는지 또는 존재하는지 정확한 길이를 서버에게 물어봐야 한다.
    • 만약 HTTP 응답을 받은 수신자가 프락시일 경우 캐시하면 안된다.(잠재적 복합에러 최소화)

12-3. 커넥션 끊기의 허용, 재시도, 멱등성

​ 커넥션은 에러가 없더라도 언제든 끊을 수 있다. HTTP 애플리케이션은 예상치 못한 커넥션 종료에 대응이 되어있어야 한다.

  • 클라이언트가 트랜잭션 도충 전송 커넥션이 끊기면, 그 트랜잭션을 재시도 하더라도 문제가 없는경우 커넥션을 다시 맺고 한번 더 전송을 시도해야한다.

  • 파이프라인 커넥션에서 클라이언트는 여러 요청을 큐에 쌓아 놓을 수 있지만, 서버는 그 요청들을 남겨둔 채로 커넥션을 끊어버릴 수도 있다.

    • 이 요청이 비멱등인 경우 클라이언트는 실제 서버에서 얼만큼 요청이 처리되었는지 알 방법이 없다. 또한 반복요청 될경우 주문 같은것이 여러번 반복되는 심한 문제가 발생한다. 때문에 비멱등한 요청들은 파이프라인을 통해 요청하면 안된다.
    • GET, HEAD, PUT, DELETE, TRACE, OPTIONS 메서드는 멱등하다.
  • 비멱등인 요청을 다시 보내야 한다면, 이전 요청에 대한 응답을 받을때까지 기다려야한다.

12-4. 우아한 커넥션 끊기

12-4-1. 전체 끊기 와 절반 끊기

​ 애플리케이션은 TCP 입력채널/출력채널 중 한개만 또는 전체를 끊을 수 있다.

img

close()를 호출하면, TCP 커넥션의 입출력 채널의 커넥션을 모두 끊는다.(전체끊기)

shutdown()을 호출하면, 입출력 채널 중 하나를 개별적으로 끊을 수 있다.

12-4-2. TCP 끊기와 리셋 에러

단순한 HTTP 애플리케이션전체 끊기만을 사용할 수 있다.

그러나

애플리케이션이 각기 다른 HTTP 클라이언트, 서버, 프락시와 통신시 그들 사이에 파이프라인 지속 커넥션을 사용하는 경우 예상치 못한 에러를 대비해 절반 끊기를 사용해야 한다.

  • 보통 커넥션을 출력 채널을 끊는 것이 안전하다.

    • 클라이언트가 데이터를 보내지 않을 것이라고 확신할 수 없는 이상, 입력 버퍼에 데이터를 저장한다.
    • 입력 채널을 끊는 경우, 클라이언트가 데이터를 보냈을때 서버의 운영체제는 TCP Connection reset by peer메시지를 클라이언트에게 보낸다.
    • 그러나 운영체제는 위의 에러를 심각한 에러로 취급하여 버퍼에 저장된 아직 읽히지 않는 데이터는 모두 삭제한다.
  • 서버가 커넥션이 충분히 오래 유지 되었다고 판단하고 연결을 끊어버린경우

    • 클라이언트가 종료된 커넥션에 데이터를 보내면, 서버는 Connection reset by peer 메시지로 응답하며 입력 버퍼에 있는 데이터는 모두 삭제한다.
12-4-3. 우아하게 커넥션 끊기

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

양쪽에서 더는 데이터를 전송하지 않을 것이라고 알려주면(출력채널의 커넥션을 끊는것), 커넥션은 리셋의 위험 없이 온전히 종료된다.

그러나 상대방이 절반 끊기를 구현했다는 보장도 없으며, 절반 끊기를 했는지 검사해준다는 보장도 없다.

따라서 애플리케이션은 절반 끊기를 하고 난 후에도 데이터나 스트림의 끝을 식별하기 위해 입력 채널에 대해 상태 검사를 주기적으로 해야한다.(자바에선 SocketChannel 키워드를 검색하면 채널 상태를 검사할수 있는 다양한 옵션들이 존재)

profile
5 Seconds rule

0개의 댓글