Tomcat
Web Container는 내부 프로그램 로직 처리를 한 후, 데이터를 만들어서 Web Server로 다시 전달한다.*Web Server에 따른 처리를 Java 진영에서는 Servlet Container
라고 부르고 있다.
Servlet Container의 예시로는 Tomcat
이 있는데, Tomcat Server가 요청을 받으면 Tomcat Engine
이 요청에 맞는 Context를 찾고,
해당 Context는 본인의 web.xml
을 바탕으로 전달받은 요청을 Servlet에 전달하여 처리되도록 한다.
Tomcat도 자바 프로그램인 만큼, 사용자의 컴퓨터에 설치된 자바 구동환경과 JVM 위에서 실행된다. 자바 프로그램 하나에 JVM 하나가 구동된다.
참고로, Spring Boot에는 Embedded Tomcat이 있어, 데이터를 처리하고 DB에 스스로 영향을 줄 수 있다.
Apache
Apache
Web Server는 멀티 프로세스와 멀티 쓰레드 방식을 함께 사용할 수 있다. 항상 idle한 수의 프로세스 및 쓰레드를 생성해두므로, 요청이 들어왔을 때 새로운 프로세스나 쓰레드가 생성되는 것을 기다릴 필요가 없다.Acceptor Thread
하나가 존재하고, 이를 관리하는 여러 개의 Thread를 동시에 띄워둔다.Acceptor Thread
가 available worker thread와 connection을 맺어준다.Tomcat engine
에 request를 보내 request를 처리하고, request header와 associated virtual host & contexts에 따른 적합한 응답을 보내달라고 한다.Q: Apache는 멀티 프로세스이고, Tomcat은 멀티 스레드입니다. 그렇다면 Apache 웹 서버 하나에 여러 개의 Tomcat Instances를 띄우면 어떨까요?
A: 여러 개의 Tomcat Instances를 띄우겠다는 것은, JVM 여러 개를 동시에 돌리겠다는 의미다. Tomcat Engine과 Instance는 원래 분리가 가능한 구조로 설계되어 있다. 그렇다면 JVM이 일정한 minimum size가 있다는 것을 생각해볼 때, memory usage 입장에서 가상 메모리 스와핑 오버헤드가 발생할 수 있다는 점 때문에 메모리 누수가 상당할 것으로 보인다. 하지만 JVM 하나 프로그램이 동작하지 않을 때, 다른 Tomcat Instance에는 영향을 받지 않는다는 장점이 될 수 있겠다. 또한, JVM 요구사항이 다른 경우에는 무조건 여러 개의 인스턴스를 띄우는게 유일한 방법이 될 것 같다.
Q: JVM이 Servlet을 실행하는 것과, 일반 자바 클래스를 실행하는 것에 차이가 있나요?
A: JVM에서의 호출 방식은 서블릿과 일반 클래스 모두 같으나, 서블릿은 main() 함수로 직접 호출되지 않는다는 차이가 있다. 서블릿은 Tomcat 같은
Web Container
에 의해 실행되는 것이다. Container가web.xml
을 읽고 서블릿 클래스를 class loader에 등록하는 절차를 밟는다.
Request
, HttpServletResponse
두 객체를 생성한다.doGet()
이나 doPost()
를 실행하게 되며Response
객체에 응답을 보낸다.Servlet Instance
는 HTTP 요청이 올 때마다 기존의 Servlet Instance를 이용한다.Servlet Instance
가 여러개의 HTTP 요청을 동시에 처리하게 된다. 따라서 Servlet Instance
는 Thread-Safe하지 않다.Servlet Instance
의 destory()
method를 호출하고 JVM의 GC(Garbage Collector)에서 Servlet Instance를 해지할 수 있도록 표시해둔다. GC는 표시된 Servlet Instance를 해지한다.결론
Apache Tomcat은 프로세스로 동작한다.
Apache는 웹서버고, Tomcat은 ServletContext인데
Apache
하나는 필요에 따라 여러 개의 Tomcat Instances를 가질 수 있다.
Tomcat
하나는 Single Servlet이다.
Tomcat
의 Instance는 각각 Instance마다 Acceptor Thread 한 개가 있고, Dedicated Thread Pool을 보유하고 있다.
Tomcat은 기본적으로 one thread per request를 주창하고 있기 때문에, HTTP Request 하나가 들어올 때 하나의 Thread를 배정한다.
-> Request가 종료되면 Thread Pool에 돌려주어 해당 Thread를 재사용할 수 있도록 한다.
Instance 하나는 여러 Thread가 공유하는 ServletContainer를 의미한다.
Servlet이 생성될 때, ServletContainer에 이미 만들어지지 않았다면 새로 만든다. Servlet을 이미 만든 적이 있다면, 이를 재사용한다. 그렇기 때문에 Servlet은 재사용이 가능한 형태로 stateless하면서 immutable하게 구현해야 한다.
상태가 없는 객체를 공유하기 때문에 별도의 동기화 과정은 필요하지 않다. 따라서 컨트롤러가 수십회건 수만회건 요청을 받아도 문제가 생기지 않는다.
Servlet의 종류에는 @WebServlet
과 Spring @Bean
이 있다. 어디서 관리하느냐에 차이다. Tomcat이 관리하냐? Spring이 관리하냐?
Bean의 경우에는 POJO(Plain Old Java Object)와 설정(Configuration Metadata)을 Spring이 제공하는 Container(DI Container, 또는 IoC Container)에 주입시키면 Bean으로 등록되고 사용이 가능하다.
결국 Spring을 쓴다는 것은 Spring으로 Servlet을 다루겠다는 뜻이다. Spring MVC 역시 Servlet Container가 관리하고 있는 Servlet이다.
➡︎ 그래서 Servlet 없이 Spring MVC만 있으면 된다고 하는것은 비지니스 로직을 Spring을 통해 처리하겠다는것이지 Servlet이 필요없다는 얘기가 아니다.
출처