- HTTP는 어떻게 TCP커넥션을 사용하는가
- TCP 커넥션의 지연, 병목, 막힘
- 병렬 커넥션, keep-alive 커넥션, 커넥션 파이프라인을 활용한 HTTP의 최적화
- 커넥션 관리를 위해 따라야 할 규칙들
💡 TCP ?
전송 제어 프로토콜(Transmission Control Protocol, TCP, 문화어: 전송조종규약)은 인터넷 프로토콜 스위트(IP)의 핵심 프로토콜 중 하나로, IP와 함께 TCP/IP라는 명칭으로도 널리 불린다.

💡 OSI ?
OSI 모형(Open Systems Interconnection Reference Model)은 국제표준화기구(ISO)에서 개발한 모델로, 컴퓨터 네트워크 프로토콜 디자인과 통신을 계층으로 나누어 설명한 것이다. 일반적으로 OSI 7 계층이라 불리기도 한다.

💡 TCP 커넥션 ?
TCP 커넥션은 클라이언트와 서버간에 주고받는 메시지들의 손실 혹은 손상되거나 순서가 바뀌지 않고 안전하게 전달할 수 있도록 한다.
TCP 커넥션의 한쪽에 있는 바이트들은 반대쪽으로 순서에 맞게 정확히 전달된다.
URL을 입력받은 브라우저가 수행하는 7단계 과정 (TCP 커넥션을 사용하는 상황 예시)
예를 들어 velog.io/@sgsg9447 라는 URL에 접속한다고 가정해보자.
이때 브라우저(클라이언트)와 서버에서 발생하는 일은 아래와 같다.
데이터의 중복이나 손실 없이 종단간 데이터의 전송을 보장함을 의미
❕어떻게?
- TCP 커넥션은 인터넷을 안정적으로 연결해준다.
- TCP 커넥션 한쪽에 있는 바이트들은 반대쪽으로 순서에 맞게 정확히 전달된다.



IP 패킷이 포함하는 것
1. IP 패킷 헤더 (보통 20byte)
발신자와 목적지 IP 주소, 크기, 기타 플래그
2. TCP 세그먼트 헤더 (보통 20byte)
TCP 포트 번호, TCP 제어 플래그, 데이터의 순서와 무결성을 검사하기 위해 사용되는 숫자 값
3. TCP 데이터 조각 (0 혹은 그 이상의 byte)
TCP 커넥션은 네 가지 값으로 식별한다.
<발신지 IP 주소, 발신지 포트, 수신지 IP 주소, 수신지 포트>
이 네 가지 값으로 유일한 커넥션을 생성한다.
소켓(Socket)이란?
프로그램이 네트워크에서 데이터를 송수신할 수 있도록 "네트워크 환경에 연결할 수 있게 만들어진 연결부"
두 프로그램이 네트워크를 통해 서로 통신을 수행할 수 있도록 양쪽에 생성되는 링크의 단자이다.
두 소켓이 연결되면 서로 다른 프로세스끼리 데이터를 전달할 수 있다.
상대방에게 데이터를 보내거나 받는 역할을 한다.
우리는 이런 TCP 커넥션으로 원격 서버의 데이터를 읽고 쓸 수 있다. 클라이언트가 서버에게 커넥션을 요청하고, TCP 커넥션이 생성되면 HTTP 요청-응답을 주고받을 수 있다.
이떄 HTTP 요청-응답은 위의 그림처럼 아래에 TCP 계층이 있기 때문에 TCP 성능에 영향을 받는다.
TCP/IP 소켓 통신이란?
클라이언트 프로그램과 서버 프로그램은 각각 자신의 포트를 통해 통신을 해야 하는데, 소켓을 통해 연결한다.
HTTP는 TCP 바로 위에 있는 계층이기 때문에, HTTP 트랜잭션의 성능은 TCP 성능에 영향을 받는다.

클라이언트는 URI 에서 웹 서버의 IP 주소와 포트를 알아내야 하는데,
해당 호스트에 방문한 적이 없다면 DNS resolution 을 통해 호스트 명을 IP 주소로 변환하는데 시간이 필요하다.
클라이언트가 서버에게 TCP 커넥션 요청을 보내고 서버가 커넥션 허용 응답을 회신하기를 기다린다.
커넥션이 맺어지면 클라이언트는 HTTP 요청을 새로 생성된 TCP 파이프를 통해 전송한다.
요청 메시지가 서버로 전달되고 서버에서 처리되는 데 시간이 소요된다.
웹 서버가 HTTP 응답을 보내는 데 시간이 소요된다.
TCP 네트워크 지연은 하드웨어 성능, 네트워크와 서버의 전송 속도, 요청과 응답 메시지의 크기, 클라이언트와 서버 간의 거리에 따라 크게 달라진다.
작은 크기의 데이터 전송에 커넥션이 사용되면 HTTP 성능을 저하시킬 수 있다.
핸드 셰이크 과정을 간단히 묘사하자면
1. 클라이언트 -> 서버 [SYN]
2. 서버 -> 클라이언트 [SYN+ACK]
3. 클라이언트 -> 서버 [확인 응답]
4. 서버 -> 클라이언드 [데이터 전송]
1-2과정이 커넥션, 3은 잘 연결되었다는 확인 응답이다.
크기가 작은 HTTP 트랜잭션은 50% 이상의 시간을 TCP 구성에 쓴다.
TCP 세그먼트안에 들어가는 데이터에 TCP 순서 번호와 체크섬이 있다.
이때 각 세그먼트의 수신자는 세그먼트를 받았을 때 확인 응답 패킷을 송신자에게 반환한다.
송신자는 확인 응답 패킷을 대기하다가 특정 시간안에 받지 못한 경우는 전송에 문제가 있는 것으로 판단하고 데이터를 다시 전송한다.
확인 응답은 크기가 작기 때문에 송출되는 데이터 패킷에 확인 응답을 piggyBack한다.
이와 같은 piggyback을 늘리기 위해서, TCP 스택은 확인응답지연 알고리즘을 구현한다.
확인응답지연 알고리즘은, 특정 시간동안 버퍼에 데이터를 저장해놓고, piggyback 하기 위한 데이터 패킷을 탐색한다. 만약 시간 내에 찾지 못하면 별도의 패킷을 만들어 전송한다.
TCP의 전송 속도는 TCP 커넥션이 만들어진지 얼마나 지났는지에 따라 달라질 수 있다. TCP 커넥션은 시간이 지나면서 전송 속도를 계속 변화시키게 되며 처음엔 속도를 제한하다가 점차 속도를 올리게 된다.
이러한 것을 TCP Slow Start라고 하며 이는 급작스러운 부하를 방지하기 위해 쓰인다.
TCP Slow Start는 처음에 커넥션이 맺어질 땐 TCP가 한 번에 전송할 수 있는 패킷의 수를 제한하게 되며 전송이 성공하면 전송 패킷 수를 증가시킨다.
즉 확인 응답을 받을 때마다 전송할 수 있는 패킷 수를 증가시키며 이 때문에 방금 만들어진 TCP 커넥션보다 어느 정도 시간이 지난 TCP 커넥션이 더 빠른 전송 속도를 갖게 된다.
물론 이를 위해서는 커넥션을 유지하도록 만들어야 한다!
TCP는 1바이트와 같은 작은 데이터라도 애플리케이션에서 전송할 수 있게 해 주는데, 만약 매번 1바이트의 데이터만 가진 패킷을 보내게 된다면 기본적으로 패킷의 헤더가 40바이트 정도 되기 때문에 매우 비효율적이다. 따라서 효율적인 데이터 정송을 위해 Nagle 알고리즘을 사용하게 되는데, Nagle 알고리즘은 패킷을 전송하기 전에 어느 정도 크기가 될 때까지 모았다가 한 번에 전송하는 알고리즘이다.
하지만 만약 현재 보내는 패킷이 마지막인데도 불구하고 패킷의 크기가 작다고 보내지 않을 경우 영원히 해당 패킷이 전송되지 않을 수 있기 때문에 모든 패킷이 확인 응답을 받았다면 크기가 작다고 해도 패킷을 전송할 수 있게 해 준다. 이러한 부분 때문에 지연이 발생하며 특히 Nagle 알고리즘은 확인 응답 지연과 함께 사용하면 성능이 매우 나빠진다.
따라서 HTTP 애플리케이션은 Nagle 알고리즘을 비활성화할 수도 있는데, 이 때 사용하는것이 TCP_NODELAY이다. 이 값을 사용하면 Nagle 알고리즘을 비활성화 할 수 있습니다.
TIME_WAIT 누적과 포트 고갈은 성능 측정을 할 때는 성능 저하를 보이지만 실제 상황에서는 문제를 발생하지 않는다. TCP 커넥션의 에지에서 TCP 커넥션을 끊으면 에지에서는 커넥션의 IP 주소와 포트번호를 메모리에 기록해둔다. 이렇게 저장해두는 이유는 같은 IP 주소와 포트번호를 사용하는 TCP 커넥션이 일정 시간 동안에는 생성되지 않게 하기 위해서인데, 보통 패킷의 최대 생명주기의 2배(2 MSL이라고 합니다.)인 2분 정도만 유지된다. 만약 이렇게 해주지 않으면 동일한 정보의 새로운 커넥션에 기존에 존재하던 패킷이 삽입되어 패킷이 중복될 수 있고 TCP 데이터가 충돌할 수 있다.
2분 동안 동일한 커넥션을 만들 수 없기 때문에 만약 발신지 포트가 최대 6만개를 사용할 수 있고 2분동안 커넥션이 재사용될 수 없다면 초당 60000 / 120 = 500개의 커넥션이 제한되게 된다. 서버가 초당 500개의 트랜잭션을 처리할 만큼 빠르지 않다면 TIME_WAIT 포트 고갈 문제는 발생하지 않는다. 물론 그렇다고 하더라도 커넥션을 너무 많이 맺거나 커넥션 정보를 메모리에 너무 많이 저장하는 것은 좋지 않기 때문에 주의해야 한다.
병렬(parallel) 커넥션
클라이언트가 여러 개의 커넥션을 맺음으로써 여러 개의 HTTP 트랜잭션을 병렬로 처리할 수 있게 하여 성능을 개선시킨다.
각 커넥션의 지연 시간이 겹치게 되므로 총 지연 시간을 줄일 수 있고, 클라이언트의 인터넷 대역폭을 한 개의 커넥션이 다 써버리는 것이 아니라면 나머지 객체를 내려받는 데에 남은 대역폭을 사용할 수 있다.
병렬 커넥션이 일반적으로 빠르긴 하지만, 클라이언트의 네트워크 대역폭이 좁을 때는 오히려 느려질 수 있다.
다수의 커넥션은 메모리를 많이 소모하고 자체적인 성능 문제를 발생시킬 수 있다.
웹 클라이언트는 보통 같은 사이트에 대해 여러 개의 커넥션을 맺는데, 이러한 특징을 사이트 지역성(site locality)라 부른다.
HTTP/1.1 을 지원하는 기기는 처리가 완료된 후에도 TCP 커넥션을 유지하여 앞으로 있을 HTTP 요청에 재사용할 수 있다.
처리가 완료된 후에도 연결을 지속하는 상태를 지속 커넥션이라고 한다. 지속 커넥션을 재사용함으로써, 커넥션을 맺기 위한 준비작업에 드는 시간을 절약할 수 있다.
병렬 커넥션의 단점
지속 커넥션의 장점
병렬 커넥션은 각 트랜잭션마다 새로운 커넥션을 맺고 끊기 때문에 시간과 대역폭이 소요된다.
각각의 새로운 커넥션은 TCP 느린 시작 때문에 성능이 떨어진다.
실제로 연결할 수 있는 병렬 커넥션 수에는 제한이 있다.
지속 커넥션은 커넥션을 맺기 위한 사전 작업과 지연을 줄여주고, 튜닝된 커넥션을 유지하며, 커넥션 수를 줄여준다.
지속 커넥션을 잘못 관리할 경우, 계속 연결된 상태로 수많은 커넥션이 쌓이게 된다.
지속 커넥션은 병렬 커넥션과 함께 사용될 때 가장 효과적이다.
timeout 커넥션이 얼마간 유지될것인지 유지될것을 의미( 이대로 동작한다는 보장은 없다)
max 커넥션이 몇 개의 HTTP 트랜잭션을 처리할 때까지 유지될 것을 의미한다. (이것도 보장 못함)
커넥션에 하나의 파이프라인을 설치해서 여러번 호출이 가능하게 만드는 것이다.
어떻게 보면 지속 커넥션과 비슷해보이지만,
다른점은 하나의 트랜잭션 요청이 종료가 되기전에 다른 트랜잭션을 호출이 가능하다는 점이다.
이 파이프라인 커넥션에는 주의점이 존재한다.
1.HTTP 클라이언트는 커넥션이 지속 커넥션인지 확인하기 전까지는 파이프라인을 이어서는 안된다.
2.HTTP 응답은 요청 순서와 같게 와야 한다.
3. HTTP 클라이언트는 커넥션이 언제 끊어지더라도, 완료되지 않은 요청이 파이프라인에 있으면 언제든 다시 요청을 보낼 준비가 되어 있어야 한다.
4. HTTP 클라이언트는 POST 요청같이 반복해서 보낼 경우 문제가 생기는 요청은 파이프라인을 통해 보내면 안된다.
POST같은 메서드를 비멱등 메서드라고 한다.
멱등이란?
연산을 여러번해도 값이 달라지지 않는 성질을 말한다.
그러니까 POST는 여러번 호출하며 결과가 달라진다는 것을 알 수 있다.
: 애플리케이션 자신의 출력 채널을 먼저 끊고, 다른 쪽에 있는 출력 채널이 끊기는 것을 기다리는 것 (절반 끊기) 하지만 상대방이 절반끊기를 구현했거나 검사한다는 보장이 없기에 출력 채널을 절반 끊기 한 후에도 데이터나 스트림의 끝을 식별하기 위해 입력 채널에 대해 주기적인 상태검사 필요
https://icksw.tistory.com/222
https://velog.io/@leeinae/HTTP-Connection-TCP
https://icarus8050.tistory.com/130
https://b-programmer.tistory.com/312
https://reakwon.tistory.com/59
https://watrv41.gitbook.io/devbook/web/http/4
https://web-km.tistory.com/17