본문 바로가기
Spring

[Spring] 서블릿(Servlet)

by jinjin98 2023. 1. 25.

̱ 서블릿(Servlet)

 

 

클라이언트가 form 데이터를 전송하면 웹 브라우저는 form 데이터를 해석해

HTTP 요청 메시지를 생성히고 서버에 전송합니다.

 

 

클라이언트 요청을 받는 WAS(웹 어플리케이션 서버) 를 직접 구현한다면

위 그림에 나와있는 모든 과정을 코드로 작성해야 합니다.

공통적이고 부수적인 로직을 제외하고 의미있는 비즈니스 로직만 작성하려면 어떻게 해야할까요?

 

 

서블릿을 지원하는 WAS 를 사용한다면 비즈니스 로직을 제외한 나머지를 자동화 시켜줍니다.

따라서 개발자는 비즈니스 로직에만 집중하여 개발할 수 있게 됩니다.

 

 

HttpServlet 을 상속하여 서블릿을 생성할 수 있습니다. 위 코드처럼 서블릿을 생성하고 /hello 로 url 요청을 하면

service() 메서드가 실행됩니다. service() 의 HttpServletRequest 파라미터는 HTTP 요청 메시지를 파싱하여

HTTP 요청 정보를 편리하게 사용할 수 있게 하고, HttpServletResponse 파라미터는 HTTP 응답 정보를 제공하여

HTTP 응답 정보를 편리하게 입력할 수 있습니다.

 

 서블릿 요청 응답 과정

 

 

앞서 작성한 HelloServlet 을 기준으로 서블릿의 동작 과정을 나타낸 그림입니다.

1. 클라이언트가 웹 브라우저를 통해 /hello 로 url 요청을 합니다.

2. HTTP 요청 메시지를 기반으로 HttpServletRequest, HttpServletResponse 객체를 생성합니다.

3. 생성된 HttpServletRequest, HttpServletResponse 객체를 urlPattern 이 /hello 인 서블릿 클래스의 service() 

파라미터에 넣어줍니다.

4. service() 가 종료되면서 HttpServletResponse 객체를 기반으로 HTTP 응답 메시지를 생성해

웹 브라우저에 전달합니다.

5. 웹 브라우저는 전달받은 HTTP 응답 메시지를 랜더링해서 클라이언트에게 보여줍니다.

 

̱ 서블릿 컨테이너

 

 

톰캣처럼 서블릿을 지원하는 WAS 를 서블릿 컨테이너라고 하며 서블릿 컨테이너는 서블릿 객체를

생성, 초기화, 호출, 종료하는 생명주기 관리를 합니다.

서블릿 객체는 싱글톤으로 관리되어 최초 로딩 시점에 서블릿 객체를 미리 생성해두고 서로 공유하며 재활용해

모든 클라이언트 요청이 동일한 서블릿 객체 인스턴스에 접근하게 됩니다.

사용자가 요청하고 응답받는 데이터가 모두 다르므로 HttpServletRequest, HttpServletResponse 객체는 요청마다

매번 새로 생성하지만, 같은 service() 를 갖고있는 서블릿 객체를 클라이언트의 요청이 올 때 마다 계속 생성하는 것은

비효율적이므로 딱 한 번만 생성해 싱글톤으로 관리하는 것입니다.

서블릿 컨테이너의 가장 큰 특징은 동시 요청을 위한 멀티 쓰레드 처리를 지원하는 것입니다.

 

̱ 쓰레드

 

 

클라이언트가 서버에 요청을 하면 서버와 TCP/ IP 커넥션이 연결되고 WAS 에서 서블릿 객체를 쓰레드가 호출합니다.

쓰레드애플리케이션 코드를 하나하나 순차적으로 실행하는 것을 말하는데요.

자바 메인 메서드를 처음 실행하면 main 이라는 이름의 쓰레드가 실행되고 자바 어플리케이션이 순차적으로

동작하게 됩니다. 그래서 쓰레드가 없다면 자바 애플리케이션 실행이 불가능합니다.

쓰레드는 한번에 하나의 코드 라인만 수행하므로 동시 처리가 필요하면 쓰레드를 추가로 생성해야 합니다. 

 

 단일 요청 - 쓰레드 하나 사용

 

 

WAS 에서 쓰레드를 하나 생성합니다.

 

 

클라이언트가 요청하면 생성된 쓰레드를 할당해 연결합니다.

연결된 쓰레드로 서블릿을 호출합니다.

 

 

 

클라이언트에게 응답을 한 후에는 쓰레드는 휴식 상태에 들어갑니다.

 

̱ 다중 요청 - 쓰레드 하나 사용

 

 

첫 번째 클라이언트가 요청해 WAS 에서 생성된 쓰레드를 할당해 연결합니다.

연결된 쓰레드로 서블릿을 호출하는데 요청을 처리하려 하는데 지연이 발생합니다.

 

 

이 때 두 번째 클라이언트가 요청하지만 연결할 수 있는 쓰레드가 없어 대기합니다.

 

 

이렇게 되면 두 요청 모두 응답을 받을 수 없어 죽게 됩니다.

 

 요청마다 쓰레드 생성

 

 

클라이언트가 요청을 하면 쓰레드를 생성하고 응답을 하면 쓰레드를 폐기합니다.

매 요청마다 쓰레드를 생성하므로 연결할 쓰레드가 없는 상황이 발생하거나 대기할 필요가 없습니다.

 

장점

1. 동시 요청을 처리할 수 있습니다.

2. 리소스(CPU, 메모리)가 허용할 때 까지 처리할 수 있습니다.

3. 하나의 쓰레드가 지연 되어도, 나머지 쓰레드는 정상 동작합니다.

단점

1. 쓰레드는 생성 비용은 매우 비싸며 클라이언트의 요청이 올 때 마다 쓰레드를 생성하면, 응답 속도가 늦어집니다.

2. 쓰레드는 컨텍스트 스위칭 비용이 발생합니다. 코어가 1개이고 쓰레드가 2개 이상이면 쓰레드를 번갈아가며

수행하는데 이 때 발생하는게 컨텍스트 스위칭 비용입니다. 이 컨텍스트 스위칭 비용은 쓰레드가의 수에 비례합니다.

3. 쓰레드 생성은 제한이 없어 클라이언트 요청이 너무 많이 오면 계속해서 쓰레드가 생성되어

, CPU, 메모리 임계점을 넘어서 서버가 종료될 수 있습니다.

 

̱ 쓰레드 풀

 

 

지금까지 살펴본 쓰레드 생성 방식의 단점들을 해결할 수 있는게 쓰레드 풀입니다. 대부분 WAS 는 쓰레드 풀을

사용하는데요. 클라이언트가 요청을 하면 쓰레드 풀에서 남아있는 쓰레드를 꺼내 연결해줍니다. 요청을 처리해 응답을

완료하면 쓰레드를 쓰레드 풀에 반납합니다. 즉, 이전처럼 요청마다 쓰레드를 생성하고 폐기하는게 아닌 빌려서

사용하고 반납하는 형식입니다.

 

 

만약 쓰레드 풀에 있는 쓰레드의 수를 초과하는 요청이 들어오게 되면

WAS 는 요청을 대기시키거나 거절할 수 있습니다.

 

특징

필요한 쓰레드를 쓰레드 풀에 보관하고 관리합니다.

쓰레드 풀에 생성 가능한 쓰레드의 최대치를 관리합니다. 톰캣은 최대 200개로 기본 설정되어 있습니다. (변경 가능)

사용

쓰레드가 필요하면, 이미 생성되어 있는 쓰레드를 쓰레드 풀에서 꺼내서 사용합니다. 사용을 종료하면 쓰레드 풀에

해당 쓰레드를 반납합니다. 최대 쓰레드가 모두 사용중이어서 쓰레드 풀에 쓰레드가 없으면 기다리는 요청은

거절하거나 특정 숫자만큼만 대기하도록 설정할 수 있습니다.

장점

쓰레드가 미리 생성되어 있으므로, 쓰레드를 생성하고 종료하는 비용(CPU)이 절약되고, 응답 시간이 빠릅니다.

생성 가능한 쓰레드의 최대치가 있으므로 너무 많은 요청이 들어와도 기존 요청은 안전하게 처리할 수 있습니다.

 

WAS의 주요 튜닝 포인트는 최대 쓰레드(max thread) 수입니다.

이 값을 너무 낮게 설정하면?

동시 요청이 많으면, 서버 리소스는 여유롭지만, 클라이언트는 금방 응답이 지연됩니다. 

이 값을 너무 높게 설정하면? 

동시 요청이 많으면, CPU, 메모리 리소스 임계점 초과로 서버가 다운됩니다.

장애 발생시? 

클라우드 환경일 때는 일단 서버부터 늘리고, 이후에 튜닝합니다.

 

쓰레드 풀의 적정 숫자?

애플리케이션 로직의 복잡도, CPU, 메모리, IO 리소스 상황에 따라 모두 다릅니다.

최대한 실제 서비스와 유사하게 성능 테스트를 시도하여 최적의 숫자를 찾을 수 있습니다.

툴: 아파치 ab, 제이미터, nGrinder..

 

핵심

멀티 쓰레드에 대한 부분은 WAS 가 처리하므로

개발자가 멀티 쓰레드 관련 코드를 신경쓰지 않아도 됩니다.

개발자는 마치 싱글 쓰레드 프로그래밍을 하듯이 편리하게 소스 코드를 작성할 수 있습니다.

하지만 멀티 쓰레드 환경이므로 싱글톤 객체(서블릿, 스프링 빈)는 주의해서 사용해야 합니다.

댓글