HwangHub

Spring MVC - 서블릿 본문

DEV-STUDY/Spring

Spring MVC - 서블릿

HwangJerry 2024. 2. 6. 21:26

자바 서블릿

자바 서블릿은 웹 통신 간 요청에 따른 응답을 동적으로 생성하는 자바 기반의 웹 어플리케이션 기술로, 흔히 "서블릿"이라 줄여 부릅니다. 여기서 말하는 웹 통신 간의 응답은 동적인 웹 페이지가 될 수도 있고, HTTP 응답 메시지일 수도 있죠. 서블릿이 하는 일을 좀 더 열거하여 말해보자면, 서블릿은 비즈니스 로직 실행을 제외하고 TCP/IP 대기 및 소켓 연결부터 HTTP 요청 메시지 헤더/바디 파싱, 그리고 HTTP 응답 메시지를 생성하여 소켓으로 전송해주는 것 까지 전부 수행하는 녀석입니다. (굉장히 많은 일을 하고 있네요)

 

@WebServlet(name = "helloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) {
        // TODO : application logic 작성
    }
}
  • urlPatterns(/hello)의 URL이 호출되면 서블릿 코드가 실행된다.
  • HTTP 요청 메시지를 편리하게 사용할 수 있는 클래스인 HttpServletRequest 지원
  • HTTP 응답 메시지를 편리하게 제공할 수 있는 클래스인 HttpServletResponse 지원

 

서블릿의 주 목적은 "개발자가 비즈니스 로직에 집중하게 하자"입니다. 우리가 맨 땅 위에 웹 어플리케이션을 직접 만든다고 가정해볼 때, 네트워크로부터 수신되는 HTTP 메시지를 파싱하여 요청을 해석해야 할 것입니다. 하지만 위에 나열한 것과 같이 HTTP 메시지를 파싱해주는 것을 중심으로, 웹 통신 간 처리를 손쉽게 할 수 있도록 만들어 줌으로써 우리는 부수적이고 반복적인 파싱 작업에 시간을 쏟는 걸 방지해줍니다. 즉, 우리는 비로소 서블릿을 이용하여 HTTP 메시지를 직접 파싱하지 않고도 HTTP 스펙을 손쉽게 사용하여 핵심적인 데이터 처리 로직에 집중할 수 있게 되는 겁니다.

서블릿 컨테이너가 있기 때문에 HTTP 요청과 응답 과정에서 다음과 같은 일이 자동으로(?) 일어납니다.

  • HTTP 요청 시 WAS(tomcat 등)는 Request, Response 객체를 새로 만들어서 서블릿 객체를 호출
  • 개발자는 Request 객체에서 HTTP 요청 정보를 손쉽게 다룰 수 있고, Response 객체에 HTTP 응답 정보를 편리하게 입력하여 전달
  • WAS는 Response 객체에 담겨 있는 내용으로 HTTP 응답 정보를 생성

서블릿 컨테이너

스프링 MVC에서는 서블릿 컨테이너를 이용하여 서블릿 객체를 생성하고, 필요할 때 호출하며, 서블릿 객체들의 생명 주기를 관리해 줍니다.

즉, 우리가 별도로 관리해주지 않아도 서블릿을 이용한 요청/응답 처리를 자동으로 톰캣이 처리/관리하고 있는 겁니다.

 

  • 톰캣과 같이 Servlet을 지원하는 WAS를 "서블릿 컨테이너"라고 함
  • 서블릿 컨테이너는 서블릿 객체를 생성, 초기화, 호출, 종료하는 생명 주기를 관리
  • 서블릿 객체는 싱글톤으로 관리됨 (최초 로딩 시점에 서블릿 객체를 만들어두고 재활용, JVM힙을 효율적으로 사용; 공유변수-싱글톤 객체의 멤버변수 사용 주의)
  • JSP도 서블릿으로 변환되어 사용
  • (중요) 동시 요청을 위한 멀티 쓰레드 처리를 지원함. 따라서 개발자가 크게 신경쓰지 않아도 WAS에서 동시 요청에 대하여 멀티쓰레드로 처리함.

멀티 쓰레드 지원

어플리케이션 코드를 하나하나 순차적으로 수행하는건 쓰레드입니다. 우리가 .java 파일을 실행시키면 main 메서드가 실행되죠? 이 때 이 main 메서드의 코드를 수행하는 녀석이 main 쓰레드입니다. 즉, 쓰레드 없이는 어플리케이션 로직을 단 한 줄도 수행할 수 없습니다.

 

이러한 쓰레드는 한 번에 한 줄의 코드 라인만 수행합니다. 따라서 여러 요청이 동시에 몰렸을 때, 이 요청을 하나의 쓰레드가 전부 처리하려면 하나의 요청을 처리하는 동안 N-1개의 요청은 대기를 해야 할 것이니, N개의 요청을 모두 순차적으로 처리하려면 O(N)만큼의 시간이 걸리겠죠. 그런데 이 요청을 각각 쓰레드를 할당하여 처리할 수 있다면 어떨까요? 이러한 아이디어를 바탕으로 "멀티쓰레드"라는 개념이 탄생하였습니다.

 

동시에 여러 작업을 처리할 필요성이 있으면 각각 여러 쓰레드를 할당하여 작업을 동시에 수행하여 효율적인 처리를 이뤄내는 것이 멀티 쓰레드의 목적입니다.

 

그렇다면 매 요청 마다 쓰레드를 생성하면 되는 걸까요?

물론 동시에 각 요청을 처리할 수 있으니 CPU와 메모리가 허락하는 한 이론적으로는 효율적인 작업이 가능하긴 할 것으로 보입니다. 하지만 분명하게 짚어야 하는 것이 있습니다.

  • 자바에서 객체를 생성하는 것은 힙에 접근하고, 메모리를 할당하고, 데이터를 연결하는 등 하드웨어 접근이 많은 매우 비싼 연산 중 하나입니다. 단순히 말해서 쓰레드 생성 및 메모리 해제는 매우 성능이 느린 작업이라는 겁니다. 따라서 매 요청마다 쓰레드를 생성하고, 작업을 수행한 뒤 메모리를 해제하는 건 연산 속도에 큰 영향을 줄 겁니다.
  • 멀티 쓰레드는 컨텍스트 스위칭 비용이 발생합니다. 단순히 말해서 CPU 코어 하나당 하나의 쓰레드를 수행할 것이므로, 10개의 멀티 쓰레드를 4개의 CPU 코어가 수행하기 위해서는 스케줄링을 적절히 하여 컨텍스트 스위칭을 통해 번갈아가며 작업을 처리해야 합니다. 당연히 컨텍스트 스위칭 비용은 쓰레드 개수만큼 계속 커집니다. (이를 CPU 자원 경합이 일어난다 하며, 결국 오버헤드가 발생하여 CPU 성능이 저하됩니다.)
  • 쓰레드는 생성에 제한이 없어서 별도의 처리 없이 모든 고객 요청에 대하여 전부 생성하다보면, 불의의 사고로 정말 많은 고객 요청이 몰릴 경우 Out Of Memory에 걸려 서버가 죽어버릴 수 있습니다.

따라서 이를 해결하기 위해 일반적으로 WAS는 쓰레드 풀이라는 걸 운영합니다. 톰캣의 경우 200개(변경 가능)의 쓰레드가 기본적으로 대기되는 쓰레드 풀을 운영하며, 요청이 있을 때 마다 대기하는 쓰레드를 할당하여 작업을 처리하고, 작업이 끝난 쓰레드는 쓰레드 풀에 반납되어 대기시킵니다. 이를 통해 쓰레드 객체를 생성하고 종료하는 과정에서 발생하는 비용을 최소화할 수 있어 요청에 대한 작업을 효율적으로 처리할 수 있습니다.

 

그렇다면 200개가 넘는 요청이 발생하면?

 

단순합니다. 대기열을 마련하여 일정 수 까지는 작업 처리를 대기시키고 나중에 처리하거나, 일정 수를 넘기는 요청에 대하여는 요청을 거절합니다.

 

쓰레드 풀의 최대 쓰레드 개수는 WAS의 주요 튜닝 포인트입니다.

  • 최대 쓰레드 수가 너무 낮게 설정되어 있다면, 메모리나 CPU에 대한 부하는 여유롭게 관리되겠지만 클라이언트의 요청은 쉽게 지연될 겁니다.
  • 그렇다고 너무 높게 설정되어 있다면, CPU나 메모리 임계점을 초과하여 서버가 죽을 수 있습니다.

따라서 클라우드 환경인 경우에는 서버 스케일링이 용이하므로 서버 대수를 늘리는 스케일아웃이 일반적인 대응법일 수 있습니다. 그리고 나서 WAS를 튜닝하는 전략이 일반적입니다. (온프레미스 환경이라면 스케일링이 그리 쉬운 이야기가 아니므로 열심히 WAS 튜닝 등 성능 튜닝을 하면 됩니다.)

 

적정 쓰레드 숫자는 상황에 따라 다르므로 제이미터나 nGrinder 등을 이용하여 실제 서비스 운영 환경과 최대한 유사하게 성능 테스트를 시도하여 쓰레드 숫자를 튜닝하는 것을 시도해볼 수 있습니다.

 

마무리

스프링부트를 이용하면 서블릿을 통해 HTTP 메시지 등을 자동으로 처리해줌과 동시에, 많은 요청을 효율적으로 처리하기 위해 톰캣이 멀티쓰레딩을 기본적으로 지원하고 있으니 우리는 비즈니스 로직을 구성할 때에도 싱글 스레드 프로그래밍을 하듯 편리하게 개발이 가능하다는 것이 스프링부트의 큰 강점이다. 다만, 멀티 쓰레드 환경이므로 싱글톤 객체를 활용할 때에는 동시성 문제가 발생할 수 있으니 항상 유의해야 한다.

 

메인 출처 : 스프링 MVC 1 (김영한)
그외 : 참고1, 참고2

Comments