Post

HTTP 완벽 가이드

목차


HTTP 완벽 가이드 책 리뷰

telnet으로 HTTP 통신하기

telnet 프로토콜 및 여러가지 CLI 툴로 HTTP Raw Request 하는 방법 정리
https://dev.iasdf.com/wiki/linux/http_cli

TCP 커넥션으로 인한 지연

나는 HTTP의 연결을 끊지 않고 유지하는 이유가
연결을 끊었다 다시 요청할 경우, 3-way/4-way handshake 인해 주로 발생하는 것이라고 생각했다.

그러나 약 1970년부터 연구되어 1983년에 개발된 글을 적고 있는 현재 기준으로 약 40년의 역사를 지닌 고인물 프로토콜 답게 TCP 프로토콜은 신뢰성 확보를 위해 내부적으로 여러가지 알고리즘이 도입되어 있는데, 이러한 알고리즘에 의해서도 지연이 발생하는 것을 알게 되었다.

  • delayed ack (확인 응답 지연)
    • http://www.ktword.co.kr/test/view/view.php?no=3242
    • TCP의 경우 신뢰성 확보를 위해 ACK 패킷을 전송해야 함. 즉 아래와 같은 통신이 사용됨
      1
      2
      3
      4
      5
      
        C              S
        | SYN ->       |
        |       <- ACK |
        |       <- SYN |
        | ACK ->       |
      
    • ACK 패킷만 바로 보내게 되면 ACK 패킷을 보내기 위한 TCP 헤더 등을 추가로 송신해야 하며, ACK 패킷을 따로 보내게 될 경우 오버헤드가 발생할 수 있음.
    • SYN 패킷을 받았을 때, ACK 패킷을 바로 전송하는 것이 아닌, 0.1초 혹은 그 이상의 대기시간을 주어 서버의 SYN 요청과 ACK 패킷을 함께 보내면 부하를 줄일 수 있음. (piggy-back 방식)
      1
      2
      3
      4
      
        C              S
        | SYN ->       |
        |   <- ACK/SYN |
        | ACK ->       |
      
    • 안타깝게도, 요청과 응답, 딱 두가지의 통신 방식만 있는 HTTP는 이러한 ACK 패킷이 다음 SYN 패킷에 편승할 기회를 감소시킴.
  • slow start
    • 클라이언트 또는 서버의 네트워크 상태를 모르니, 혼잡이 발생하지 않도록 커넥션이 처음 발생했을 때 패킷을 전송하는 양을 제한하고, 이를 천천히 늘려나가며 네트워크 혼잡을 제어함.
    • 이를 TCP 혼잡 제어라고 하는데, 이 때문에 TCP의 커넥션이 막 연결될 당시에는, 혼잡 제어 알고리즘으로 인해 이미 연결된 알고리즘에 비해 속도 저하가 발생함.
    • 즉, HTTP의 커넥션을 유지하지 않으면, 새로운 요청을 진행할 때메다 tcp 커넥션을 연결하고 끊기 때문에, slow start로 인해 지연이 발생할 수 있음.
  • Nagle(네이글) 알고리즘과 TCP_NODELAY
    • 매우 작은 데이터를 보내야 하는 경우, (예를 들어 1~10byte 정도) Data의 내용보다 이 내용을 보내기 위한 TCP 헤더의 정보가 더 크다.
    • 따라서 이러한 작은 패킷들을 모아, 묶어서, 전체 크기의 세그먼트를 전송할 수 있을 때까지 패킷의 전송을 지연시킬 수 있음.
      • MTU(Maximum Transmission Unit) 사이즈는 일반적으로 1500 byte 이므로, MSS(Maximum Segment Size)는 TCP 헤더와 IP 헤더의 길이를 제외한 1460 byte 이다.
    • 네이글 알고리즘은 delayed ack와 함께 쓰일 경우 문제를 발생시킨다.
      • delayed ack는 확인 응답을 100~200 밀리초 지연시키는데, 네이글 알고리즘은 확인 응답이 도착할 때 까지 데이터 전송을 멈추기 때문에 서로 상반되는 제약조건을 가지고 있다.
    • 또한 네이글 알고리즘은 MSS 사이즈의 패킷을 채우지 못하는 크기가 작은 HTTP 메시지의 경우, 앞으로 생길지 알 수 없는 추가적인 데이터를 기다리며 지연시킬 것이다.

    • 따라서 HTTP 어플리케이션은 성능 향상을 위해 TCP_NODELAY 옵션을 주어, 네이글 알고리즘을 비활성화 하기도 한다.
      • 이 설정을 진행했다면 작은 크기의 패킷이 많이 생기지 않도록 큰 크기의 데이터를 만들어야 한다.
  • TIME-WAIT 누적, 포트 고갈
    • TCP는 4way handshake 이후에도 연결을 바로 종료하는 것이 아니라, TIME_WAIT을 통해 잠시 대기 상태에 들어간다.
    • A가 FIN을 보내고 B가 ACK, FIN을 보낸 뒤, A가 ACK를 보내고, A 또한 연결을 바로 끊는 것이 아니라 TIME_WAIT 상태에 진입한다.
      • 이유인 즉, 혹시 마지막에 B에게 보낸 ACK 메시지가 도착하지 않았을 경우, B는 A에게 다시 FIN/ACK 메시지를 보내게 된다.
      • A는 TIME-WAIT 상태에서 기다리다가 FIN/ACK 메시지를 받게 되면 TIME-WAIT 상태에서 빠져나와 B에게 다시 ACK 메시지를 보내고, 다시 TIME-WIAT 상태로 돌입한다.
    • 즉, 먼저 연결 종료를 요청할 경우 TIME-WAIT 상태에 들어간다.
    • 마지막 ACK 패킷이 전송되었는지를 확인하기 위해 거치는 작업. (혹시 모를 전송 실패에 대비하기 위해 TIME-WAIT이 존재)
    • TIME WAIT이 설명된 문서 : https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      
        A              B
        | FIN ->       |
        |       <- ACK |
        |       <- FIN |
        | ACK ->       |
        |(TIME-WAIT)   | (소켓 소멸)
        |(TIME-WAIT)   |
        |(TIME-WAIT)   |
        |(TIME-WAIT)   |
        |(TIME-WAIT)   |
        |(TIME-WAIT)   |
        |(소켓 소멸)     |
      
    • 어느 쪽에서 FIN 패킷을 먼저 보내는가에 따라 발생 케이스가 달라질 수 있다.
    • TIME WAIT 과 포트 고갈로 인한 문제 발생 사례 : https://tech.kakao.com/2016/04/21/closewait-timewait/
    • 일반적인 상황에서는 발생하지 않을 가능성이 크나, 성능측정과 같이 제한적인 컴퓨터 대수로 많은 요청을 진행할 경우, TIME_WAIT 의 누적과 포트 고갈로 인해 문제가 발생할 수 있음.
    • 이러한 문제를 해결하기 위해 TIME WAIT 상태의 소켓을 재사용하는 net.ipv4.tcp_tw_reuse 옵션 또는 net.ipv4.tcp_tw_recycle 옵션을 사용할 수 있다.
    • 물론 이 외에도, 많은 양의 커넥션을 맺어두거나 대기 상태로 있는 제어 블록이 많아지는 상황은 조심해야 함.

책 짤막 회고

HTTP의 기반 통신이 되는 TCP 커넥션을 위주로 살펴보았다. 인상적이었던 부분은 telnet을 응용하여 HTTP RAW Request를 보내는 방법을 실습해보았고, 이를 통해 실제 HTTP RAW Packet을 보다 직접적으로 보내 보다 쉬운 이슈 재현 환경을 만들 수 있었다. 웹서버의 성능을 높이고 부하분산, 보안을 위해 프록시, 캐시서버, 게이트웨이, 터널 등 여러가지 방법을 사용할 수 있다는 것을 다시금 확인할 수 있었다.

책이 옛날에 쓰여진 책이라서 현재의 HTTP 프로토콜과 달라진 점을 비교, 조사해가며 읽었다.

  • HTTPS의 과거 SSL과 현재 TLS의 차이
  • HTTP/1.1과 이를 보완한 HTTP/2, UDP 기반의 QUIC (HTTP/3) 프로토콜을 살펴보며, HTTP의 발전사를 다시금 공부하였다.

HTTP와 TCP의 프로토콜에 대해, 그리고 네트워크 트래픽이 어떻게 흐르는지 이해하고 복습하는데 도움이 되었다.

  • 리다이렉트를 통한 부하분산 처리
  • 정적 페이지와 리소스를 캐싱하여 성능을 향상시키는 방법
    • 캐시의 동작과 만료에 대한 이해
  • 프록시 서버를 통한 트래픽 분산과 보안 강화
    • 프록시의 커넥션(클라이언트와 프록시, 프록시와 웹서버) 관리 방법에 대한 이해
  • 터널링을 통한 기타 프로토콜과의 연결
This post is licensed under CC BY 4.0 by the author.