- HTTP 구조
- Keep Alive란?
- Keep Alive 특징
- Keep Alive 성능 측면
- Keep Alive 문제점
- Keep Alive 사용법
- Keep Alive 예시
- Summary
- Reference
HTTP 구조
-
HTTP는 Connectionless 방식으로 연결을 매번 끊고 새로 생성하는 구조이다.
-
이는 Network 비용 측면에서 최초 연결을 하기 위해 많은 비용을 소비하는 구조이다.
Keep Alive란?
-
HTTP/1.1부터는
-
이미 연결되어 있는 TCP 연결을 재사용하는
-
Keep-Alive라는 기능을 Default로 지원한다.
-
즉 Handshake 과정이 생략되므로 성능 향상을 기대 할 수 있다.
-
아래의 예는 HTTP 1.1 Keep-Alive 기능에 대해글에서 발췌했다.
Keep Alive 사용 X
$ telnet pungjoo.com 80
Trying 121.124.124.74...
Connected to pungjoo.com.
Escape character is '^]'.
GET / HTTP/1.0
Host: pungjoo.com
HTTP/1.1 302 Moved Temporarily
Date: Tue, 15 Jan 2008 01:33:56 GMT
Set-Cookie: JSESSIONID=B4329BFEDB1363BB90FCFA9568DDBF0B; Path=/
Location: http://pungjoo.com/servlet/com.pungjoo.blog2005.Action
Content-Type: text/html;charset=EUC-KR
Content-Length: 0
Connection: close
Connection to pungjoo.com closed by foreign host.
-
Response 메시지와 함께
-
Connection to pungjoo.com closed by foreign host. 메시지가 노출된다.
-
즉 Response 메시지와 함께 커넥션이 종료되었음을 알 수 있다.
Keep Alive 사용 O
$ telnet pungjoo.com 80
Trying 121.124.124.74...
Connected topungjoo.com.
// [1]
GET / HTTP/1.1
Host: pungjoo.com
Connection: Keep-Alive
HTTP/1.1 302 Moved Temporarily
Date: Tue, 15 Jan 2008 01:36:26 GMT
Set-Cookie: JSESSIONID=2B3EE2FE56868BD9588A7B55C405974B; Path=/
Location: http://pungjoo.com/servlet/com.pungjoo.blog2005.Action
Content-Type: text/html;charset=EUC-KR
Content-Length: 0
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
// [2]
GET / HTTP/1.1
Host: pungjoo.com
Connection: Keep-Alive
HTTP/1.1 302 Moved Temporarily
Date: Tue, 15 Jan 2008 01:36:35 GMT
Set-Cookie: JSESSIONID=E4E3D9B9CFE3693DC5AFC3D6ABDE5564; Path=/
Location: http://pungjoo.com/servlet/com.pungjoo.blog2005.Action
Content-Type: text/html;charset=EUC-KR
Content-Length: 0
Keep-Alive: timeout=5, max=99
Connection: Keep-Alive
// [3]
Connection to pungjoo.com closed by foreign host.
-
Telnet(연결)은 한 번만 이뤄졌고
-
GET Method는 2번([1],[2]) 요청했다.
-
즉 이렇게 할 수 있는 이유는 Keep Alive 기능 때문이다.
-
[2] : Keep-Alive: timeout=5, max=99 부분에서 max가 감소를 하는 것을 볼 수 있다.
-
이는 최초 연결된 Port에 대해서 기존엔 max=100이였지만
-
해당 시점에 1번의 요청이 더 이뤄졌기 때문에 추가적으로 99번에 Request가 가능하단 뜻이다.
-
[3] : Connection to pungjoo.com closed by foreign host. 메시지
-
해당 메시지는 바로 나오는 것이 아니라
-
설정한 timeout(=5초)이 지난 후에 나오게 된다.
-
즉 Server/Client의 연결이 5초간 유지되었음을 알 수 있다.
Keep Alive 특징
-
Keep Alive의 유지 시간은
-
연결된 Socket에 I/O Access가 마지막으로 종료된 시점부터
-
정의된 시간까지 Access가 없더라도 세션을 유지하는 구조이다.
-
즉 정의된 시간내에 Access가 이루어진다면 계속 연결된 상태를 유지할 수 있게 된다.
Q. Keep Alive Timeout 설정은 왜 필요한가?
-
서버 자원은 무한정이 아니다.
-
그렇기 때문에 이러한 접속을 계속 유지하는 것은 Server에 손실을 발생시킨다.
-
즉 서버와 연결을 맺을 수 있는 Socket은 한정되어 있고
-
연결이 오래 지속되면 다른 사람들이 연결을 못하게되는 상황이 닥친다.
Keep Alive 성능 측면
장점
출처 : HTTP 완벽가이드 - 4.5.2 HTTP/1.0+의 Keep-Alive 커넥션
-
위 이미지는 4개의 트랜잭션에 대해
-
연속적으로 4개의 커넥션을 생성하여 처리하는 방식과
-
하나의 지속 커넥션으로만 처리하는 방식을 비교한 이미지이다.
-
후자에서는 커넥션을 맺고 끊는 데 작업이 없어졌기 때문에 시간이 단축되었다.
단점
-
정적 자원(HTML, 이미지 파일 등)으로만 구성된 웹 서버에
-
Keep Alive을 사용할 경우 약 50%의 성능 향상을 보인다고 한다.
-
위와 같은 성능 향상을 보이려면
-
서버가 바쁘지 않아야 하는데
-
바쁜 서버 환경에서 Keep Alive 기능을 사용할 경우
-
모든 요청 마다 연결을 유지해야 하기 때문에
-
프로세스 수가 기하급수적으로 늘어나 MaxClient값을 초과하게 된다.
-
따라서 메모리를 많이 사용하게 되며 이는 곧 성능 저하의 원인이 된다.
-
즉 대량 접속 시 효율이 떨어지게 된다.
Keep Alive 문제점
-
Keep-Alive 기능은 설계상 문제가 있었다.
-
그리고 그 설계상 문제를 100% 해결하지 못하였고
-
그렇기 때문에 HTTP/1.1 명세에서는 제외되었다.
-
하지만 한번 생성된 커넥션을 재사용한다는 개념은 유지되었다.
문제점
멍청한(Dumb) 프록시
-
일반적으로 HTTP는 클라이언트와 서버 사이에
-
프록시 서버, 캐시 서버 등과 같은 중개 서버가 놓이는 것을 허락한다.
-
그런데 여기서 프록시 서버가 멍청할 경우 문제가 발생한다.
-
멍청한 프록시는 Connection 헤더를 이해하지 못하고
-
해당 헤더들을 삭제하지 않고 요청 그대로를 다음 프록시에 전달한다.
-
예를 통해 알아보자.
출처 : HTTP 완벽가이드 - 4.5.6 Keep-Alive와 멍청한(dumb) 프락시
Step 1
-
웹 클라이언트는 프록시에 Connection : Keep-Alive 헤더와 함께 메시지를 전송한다.
-
클라이언트는 커넥션을 유지하자는 요청에 대한 응답을 확인하기 위해 기다린다.
Step 2
-
멍청한 프록시는 요청받은 HTTP의 Connection 헤더를 이해하지 못한다.
-
프록시는 Keep-Alive를 모르기 때문에 다음 서버에 메시지를 그대로 전달한다.
-
하지만 Connection 헤더는 홉별(hop-by-hop) 헤더이다.
-
여기서 부터 문제가 시작된다.
Step 3
-
(b)에서 보이는 것처럼 전달된 HTTP 요청이 서버에 도착한다.
-
웹 서버가 프록시로부터 헤더를 받으면
-
프록시가 커넥션을 유지하기를 원하는 걸로 잘못 판단하게 된다.
-
서버는 프록시와 커넥션을 유지하는 것에 동의를 하고
-
Connection : Keep-Alive 헤더를 포함하여 응답한다.
-
하지만 프록시는 그 헤더를 이해하지 못한다.
Step 4
-
멍청한 프록시는
-
서버로부터 받은 Connection : Keep-Alive 헤더를 포함하고 있는 응답 메시지를 클라이언트에게 전달한다.
-
클라이언트는 이 헤더를 통해
-
프록시가 커넥션을 유지하는 것에 동의했다고 생각한다.
-
하지만 Keep-Alive를 이해하지 못한 프록시는 그렇지 않다.
Step 5
-
멍청한 프록시는 서버로 받은 모든 데이터를
-
클라이언트에게 전달 후 커넥션이 끊어지기를 기다린다.
-
하지만 서버는 프록시가 자신에게 커넥션을 유지하기를 요청한 것으로 알고 있기 때문에 커넥션을 끊지 않는다.
-
또한 클라이언트는 응답 메시지에 Keep-Alive가 있기 때문에
-
다음 요청을 보내기 시작할 때 커넥션이 유지되고 있는 프록시에 요청을 보낸다.
-
하지만 프록시는 같은 커넥션상에서 다른 요청이 오는 경우는 예상하지 못하기 때문에
-
그 요청은 프록시로부터 무시되고 브라우저는 응답 없이 대기를 하게 된다.
-
그래서 브라우저는 무한정 대기를 하다 타임아웃이 나서 커넥션이 끊기게 된다.
차선책
-
멍청한 프록시가 무조건 전달하는 문제를 해결하기 위해
-
개발자들은 Proxy-Connection이라는 헤더를 사용하는 차선잭을 제시하였다.
-
브라우저에서 일반적으로 전달하는 Connection 헤더 대신에
-
비표준인 Proxy-Connection 확장 헤더를 프록시에 전달한다.
-
프록시가 Proxy-Connection 헤더를 무조건 전달하더라도
-
웹 서버는 그것을 무시하기 때문에 문제가 되지 않는다.
-
하지만 Keep-Alive를 이해하는 프록시는
-
Proxy-Connection 헤더를 Connection 헤더로 변경하여 지속 커넥션을 유지할 수 있게 된다.
출처 : HTTP 완벽가이드 - 4.5.7 Proxy-Connection 살펴보기
차선책의 한계
-
위 방식은 클라이언트 <-> 서버 사이에
-
1개의 프록시만 있는 경우에만 동작한다.
-
만약 멍청한 프록시가
-
똑똑한 프록시 옆에 존재한다면 똑같은 문제가 발생한다.
출처 : HTTP 완벽가이드 - 4.5.7 Proxy-Connection 살펴보기
Keep Alive 사용법
Version
HTTP/1.0+
Client(Browser)는 http 1.1을 준수하고 이해 할 수 있다고
Request에 Connection헤더에 Keep-Alive 값을 넣어 Server에 전송한다.
ex) Connection : Keep-Alive, ...
Request를 받는 Server는
Kepp Alive 기능을 활성화하고
Keep Alive Timeout을 설정한다.
HTTP/1.1
- Default로 동작을 하므로 설정에 신경을 쓰지 않아도 된다.
HTTP/1.0+ vs HTTP/1.1
-
1.0+에서는 Keep-Alive를 사용하기 위해 설정을 해야했다면
-
1.1에서는 Keep-Alive를 끊기 위해 설정을 해야한다.
-
즉 설정의 목적이 사용하기 위함이냐 끊기 위함이냐로 생각하면 된다.
주의 사항
-
단 모든 TCP 세션을 무한정 유지할 수는 없으므로 Timeout 및 Max 설정을 통해 관리되어야 한다.
-
Keep Alive Timeout : 요청에 대한 응답을 보낸 후 Timeout을 위한 timer가 동작한다.
-
최근에는 N/W 환경이 개선되면서 Keep Alive Timeout이 점점 줄어드는 추세이다.
-
Event-driven 구조여서 non-blocking을 사용하는 Nginx 등은
Keep Alive를 하면서도 Thread를 점유하지 않기 때문에 동시 처리에 유리하다.
-
Keep Alive 예시
-
Image를 4개를 보여주어야 한다.
-
Client는 동시에 2개의 Image만 받아올 수 있다.
-
Image는 받아오는데 2초가 걸린다.
-
Port를 여는데 1초가 걸린다.
Keep Alive : False
처음 Server에 2개의 Port를 열고 Image를 받고 Client Socket의 닫는다. ( 3초 )
다시 Server에 2개의 Port를 열고 Image를 받고 Client Socket의 닫는다. ( 3초 )
총 6초가 걸린다.
Keep Alive : True
처음 Server에 2개의 Port를 열고 Image를 받는다. ( 3초 )
재요청 시 기존에 열어 둔 Port로 Image를 받아온다. ( 2초 )
총 5초가 걸린다.
Summary
-
2018.10.07 일에 처음으로 글을 작성하였고
2020.03.31 일에 내용을 추가하였다.
그래서 글이 굉장히 길어졌지만
그만큼 퀄리티는 더 높아졌다.
-
그럼에도 한 번에 읽기가 다소 부담스러울 수 있으니
충분히 나눠서 읽으면서 확실하게 이해를 하였으면 좋겠다.