kujaHn 2021. 7. 31. 21:29

대화를 하려면 말이 통해야 합니다. (당연한 말이지만..)

우리는 휴대폰을 통해 물리적인 거리를 뛰어넘어 대화가 가능합니다.

단, 이 전화선(통신)이 잘 연결되어있단 가정하에 말이죠.

전화선(통신)이 끊겼다 생각해봅시다.

그림1 전화선의 중요성

당연히 대화를 하지 못합니다.

컴퓨터 역시 마찬가지입니다. 컴퓨터끼리 데이터를 주고받아 트랜잭션을 수행하려면, 전화선이 필요합니다.

이를 커넥션(Connection)이라고 합니다!

 

 

커넥션

커넥션의 연결방법은 다음과 같은 과정을 거칩니다.

  1. 클라이언트가 서버의 URI로부터 호스트명 추출.
  2. DNS를 통해 이 호스트명에 대한 IP 주소를 찾는다.
  3. 추가적으로 포트 번호 얻는다.
  4. 해당 IP주소와 포트 번호로 커넥션을 생성한다.

결과적으로 <발신지 IP주소, 발신지 포트, 목적지 IP주소, 목적지 포트> 정보를 가지고 커넥션을 생성하게 됩니다.

각 요소의 중복은 허용되나 모두 같으면 안 된다는 것에 유의합시다!

 

이후 커넥션이 성공적으로 생성이 되면, IP 패킷이라고 불리는 작은 조각을 통해 데이터를 전송합니다.

이 IP 패킷 안에는 세그먼트라는 데이터 스트림 단위들이 들어있습니다. IP 패킷을 큰 택배 상자로 생각하는 게 좋겠네요.

그림2 IP패킷

우리가 이전에 배운 메시지 TCP 데이터 스트림 안에 포함되어 있습니다.

 

커넥션 생성

컴퓨터끼리는 IP 패킷에 담긴 TCP 패킷(TCP 세그먼트)으로 서로 간의 커넥션의 상태를 조정합니다.

이 패킷 안에는 플래그[각주:1]가 담겨있어 커넥션을 어떻게 조정할지 정할 수 있습니다.

 

종류 설명
SYN(Synchronization) 연결 요청 커넥션 생성을 요청하는 플래그.
 커넥션 생성 시 가장 먼저 보내는 플래그다.
ACK(Acknowledgement) 응답 패킷을 받았다는 것을 상대방에게 알려주는 플래그
주로 다른 플래그와 함께 보낸다. (+1 느낌) 상대방은 이를 보고 성공 실패 유무를 판단.
RST(Reset) 재 연결 종료 - 비 정상적인 연결로 확인되어 즉시 연결을 끊고자 할때 사용하는 플래그다.
양방향에서 동시에 일어난다.
PSH(Push) 밀어넣기 - 빠른 응답이 중요한 경우, 즉시 패킷을 전달하도록 명령하는 플래그.
URG(Urgent) 긴급 데이터 - 긴급히 전달해야 할 내용이 있는경우에 전달하도록 명령하는 플래그.
FIN(Finish) 연결 종료 요청 - 더이상 전송할 데이터가 없어 커넥션을 종료하도록 명령하는 플래그.

 

그럼 전체적인 트랜잭션 과정을 살펴봅시다!

그림3 전체적인 트랜잭션 과정

 

다음으로 커넥션에 대해 중요한 몇몇 가지를 짚고 넘어가 봅시다.

1. 커넥션은 안정적이어야 한다.

통신이 약한 곳에서 전화기로 대화를 하다가 자주 통화가 끊기는 경험이 있을 겁니다. 다행히 사람은 한두 번씩 전화가 끊어져도 그냥 전화를 다시 걸어 대화를 이어나가면 되지만, 안타깝게도 0과 1밖에 알지 못하는 컴퓨터는 그렇지 못합니다. 데이터가 완전 깨져버려 쓰지 못하게 되어 처음부터 새로 데이터를 전송해야 합니다.

 

그렇기 때문에 커넥션의 안정성(Stability)은 매우 중요합니다.

 

다행히도 TCP/IP는 매우 안정적인 통로여서 데이터를 안전하게 전송할 수 있습니다.

추가적으로 TCP 세그먼트에 담긴 체크섬을 이용해 데이터의 무결성을 체크합니다. 이를 통과하면 확인 응답을 전송하는데, 만약 보낸 측에서 일정 시간 안에 받지 못했다면, 데이터에 문제가 있다고 간주하고 재전송을 실시합니다.

 

그림4 TCP 체크섬과 확인응답

2. HTTP 트랜잭션의 성능은 TCP의 성능에 영향을 받는다.

데이터를 주고받아 작업을 처리하는 트랜잭션은 당연히 TCP의 성능에 영향을 받습니다.

그래서 HTTP 트랜잭션의 성능을 향상하기 위해서는 TCP의 성능을 개선할 필요가 있습니다.

개선을 하려면 문제점을 알아야 합니다. 이번에는 문제점을 파악해봅시다!

'혼잡 제어'를 검색하면 더 자세한 내용을 얻을 수 있습니다!

1) 확인 응답 지연 알고리즘

TCP는 체크섬과 확인응답를 주고받아 데이터의 신뢰도를 높인다고 얘기했었습니다.

 

여기서 고려해야 할 점은 데이터는 반드시 패킷에 담아서 전송해야 한다는 점입니다.

 

확인 응답의 크기는 IP 패킷과 비교하기에 매우 작습니다. 그렇기에 만들고 보내는 비용 역시 차이가 날 수밖에 없습니다.

만약 IP 패킷에 확인 응답만 담아 보낸다면? 배보다 배꼽이 큰 상황이 발생하게 되겠죠.

(10L 종량제 봉투에 계란 껍데기 하나만 묶어 버린다고 생각해 봅시다.)

그림5 확인응답만 전송하는 비효율적인 패킷

이러한 상황을 방지하기 위해 새로운 전략을 내놓기 시작합니다.

바로 일정 시간(0.1~0.2초) 동안 확인 응답을 버퍼에 저장해 두고 다른 데이터를 전송하는 김에 확인 응답을 끼워 넣는 [각주:2]이죠. 이를 확인 응답 지연이라고 합니다.

이 방법을 사용한다면 불필요한 자원 소모를 줄일 수 있습니다.

하지만 여기에도 약점이 존재합니다.

 

같이 보낼 데이터가 없다면?

 

이런 상황이 발생하게 되면, 확인 응답 알고리즘을 적용한 컴퓨터는 편승할 데이터를 찾느라 대기할 것이고 이는 그대로 의도치 않는 지연을 발생하게 됩니다.

이 지연이 한 번만 발생한다면(0.1~0.2초) 사소할지라도 계속해서 누적이 된다면 상당한 시간이 되니 주의를 해야 합니다.

그림6 확인응답 지연

다행히도 운영체제에 따라 확인 응답 지연 기능을 수정하거나 비활성화할 수 있습니다.

하지만 잘못 건드리면 심각한 오류를 발생하니, 잘 알아보고 확신을 가져 수정해야 합니다!

2) TCP 느린 시작

길들여야 사용성이 좋아지는 도구들이 있습니다. 야구 글러브와 무쇠로 만든 가마솥, 팬 등이 이에 속하죠.

TCP 커넥션도 예외는 아닙니다! 생성 초기에는 최대 속도를 제한해 두었다가 데이터를 전송할수록 속도 제한을 높여나가는 기술을 'TCP 느린 시작'이라고 합니다. 생성 초기의 갑작스러운 부하와 혼잡을 방지하는데 기여를 합니다.

구체적인 기술을 보자면 TCP가 한 번에 전송할 수 있는 패킷의 수를 제한하는 방식으로 속도를 제한하는데

하나의 패킷의 교환을 성공적으로 했다면 그다음부터는 2개씩 패킷을 늘려나가는 방법으로 점차 늘려가는 방식이죠.

 

그림7 TCP 느린시작

3) 네이글 알고리즘

확인 응답 지연 전략을 취하게 된 이유가 데이터 크기가 얼마가 되었든 패킷에 반드시 담아서 전송점을 보완하기 위함이라 했습니다. 네이글 알고리즘 역시 같은 목적으로 만들어진 전략입니다.

 

이왕 보낼 거 한 번에 모아서 전송하자!

 

간단한 수학 문제입니다. 둘 중 무엇이 더 크나요?

Q1)  (1 + 40) X 40 = 1640
Q2)  (40 + 40) X 1 = 80

당연히 Q1입니다. 여기서 몇 글자만 더 추가해 봅시다

그림8 네이글 알고리즘

이것이 핵심입니다. 네이글 알고리즘은 확인 응답이 올 때까지 최대 크기의 데이터를 모을 때까지 전송을 보류해두었다가 그 기준치를 넘으면 전송하는 방식입니다. 이 역시 좋아 보이지만 한계점이 명확합니다.

 

Q. 최대 크기를 채울 때까지 무한정 대기할 것인가?

 

최대 크기를 채우지 못한다면, 이를 채우기 위해 대기를 하게 될 텐데 문제는 추가적인 데이터가 언제 올지 모른다는 점입니다. 비용을 줄이자고 사용자에게 큰 불편함을 준다면, 아무 의미가 없는 기술입니다.

 

이를 해결하기 위해 TCP_NODELAY 파라미터 값을 재설정해 네이글 알고리즘을 비활성화하는 방식을 취할 수 있습니다.

단 이 경우 패킷이 너무 많이 생기지 않도록, 적당히 큰 데이터를 만들도록 조정할 필요가 있습니다.

 

4) TIME_WAIT, 포트 고갈

이전 사용자와 관련된 데이터가 다른 사용자에게 간다면 심각한 문제가 발생하게 됩니다. 이를 방지하기 위해 TCP 커넥션을 끊을 때 서버의 IP 주소와 포트 번호를 제어 영역에서 기록해, 해당 IP주소와 포트번호에서 커넥션이 생성되지 않도록 패킷의 생명주기의 두배 정도의 유예기간을 두게 됩니다. 

평상시에는 이 기술이 문제가 되지 않지만 개발, 성능 테스트 단계에서 종종 문제가 발생합니다.

 

이유는 부하를 주는 컴퓨터의 수도 적고 서버 전체를 가용하는 것이 아닌 일부만을 가용하기 때문입니다.

 

앞서 커넥션은 <발신지 IP주소, 발신지 포트, 목적지 IP주소, 목적지 포트>의 정보를 가진다고 했습니다.

테스트를 하는 경우에는 몇 개의 요소는 고정되고 하나만 돌아가며 사용하는 경우가 많죠.

(ex 발신지 포트 or 목적지 포트만 돌아가며 테스트)

이 경우 만들 수 있는 커넥션의 경우의 수는 확 줄어들게 됩니다. 결국 포트 고갈을 불러일으키게 되고 지연을 발생하게 됩니다.

 

다음 시간에는 이에 대한 해결책과 개선점을 알아보도록 합시다!

 

참고자료

도서 : HTTP 완벽 가이드
웹사이트 : MDN Web Docs (mozilla.org)

 

MDN Web Docs

The MDN Web Docs site provides information about Open Web technologies including HTML, CSS, and APIs for both Web sites and progressive web apps.

developer.mozilla.org

 

 

주석

  1. 커넥션 관련 동작을 수행하는 신호. 깃발로 신호를 보내는 것이 유래. [본문으로]
  2.  편승(PiggyBack)라고도 합니다 [본문으로]