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
옵션을 사용할 수 있다. - 물론 이 외에도, 많은 양의 커넥션을 맺어두거나 대기 상태로 있는 제어 블록이 많아지는 상황은 조심해야 함.
This post is licensed under CC BY 4.0 by the author.