Tomcat과 Apache 그리고 Thread

0

개념정리

목록 보기
6/33

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한 수의 프로세스 및 쓰레드를 생성해두므로, 요청이 들어왔을 때 새로운 프로세스나 쓰레드가 생성되는 것을 기다릴 필요가 없다.
    평소에는 요청 하나가 쓰레드 하나에 대응을 하다가, 사용자의 접속이 증가하면 Apache MPM 방식에 따라 프로세스를 fork하거나 쓰레드를 할당한다.

  • 이와 달리 Tomcat은 멀티 쓰레드 방식을 사용한다. 쓰레드 풀을 관리하고 있는 Acceptor Thread 하나가 존재하고, 이를 관리하는 여러 개의 Thread를 동시에 띄워둔다.
    • 클라이언트로부터 요청이 들어오면, Acceptor Thread가 available worker thread와 connection을 맺어준다.
      worker thread는 응답을 받아 Tomcat engine에 request를 보내 request를 처리하고, request header와 associated virtual host & contexts에 따른 적합한 응답을 보내달라고 한다.
      이후 Tomcat은 client와의 socket 통신이 열리면 다시 worker thread를 활성화 한다.

Q: Apache는 멀티 프로세스이고, Tomcat은 멀티 스레드입니다. 그렇다면 Apache 웹 서버 하나에 여러 개의 Tomcat Instances를 띄우면 어떨까요?

A: 여러 개의 Tomcat Instances를 띄우겠다는 것은, JVM 여러 개를 동시에 돌리겠다는 의미다. Tomcat Engine과 Instance는 원래 분리가 가능한 구조로 설계되어 있다. 그렇다면 JVM이 일정한 minimum size가 있다는 것을 생각해볼 때, memory usage 입장에서 가상 메모리 스와핑 오버헤드가 발생할 수 있다는 점 때문에 메모리 누수가 상당할 것으로 보인다. 하지만 JVM 하나 프로그램이 동작하지 않을 때, 다른 Tomcat Instance에는 영향을 받지 않는다는 장점이 될 수 있겠다. 또한, JVM 요구사항이 다른 경우에는 무조건 여러 개의 인스턴스를 띄우는게 유일한 방법이 될 것 같다.

  • MSA 기반에 따라 서비스별로 나눠진 WAR 파일을 배포해야 한다고 가정해보자. 그렇다면 하나의 톰캣 엔진으로 여러 인스턴스를 띄워서 WAR 파일 개수만큼 인스턴스를 구동시킬 수 있을 것이다. 단, 이럴 경우 여러 개의 인스턴스가 모두 동일한 하나의 톰캣 위에서 운영되니 해당 톰캣에 문제가 발생할경우 모든 인스턴스에 문제가 전파가 될 것이다.

Q: JVM이 Servlet을 실행하는 것과, 일반 자바 클래스를 실행하는 것에 차이가 있나요?

A: JVM에서의 호출 방식은 서블릿과 일반 클래스 모두 같으나, 서블릿은 main() 함수로 직접 호출되지 않는다는 차이가 있다. 서블릿은 Tomcat 같은 Web Container에 의해 실행되는 것이다. Container가 web.xml을 읽고 서블릿 클래스를 class loader에 등록하는 절차를 밟는다.

  • HttpRequest가 Servlet Container로 새로이 들어오면 Servlet Container는 HttpServletRequest, HttpServletResponse 두 객체를 생성한다.
    • GET/POST 여부에 따라 doGet() 이나 doPost()를 실행하게 되며
      동적 페이지 생성 후에, HttpServletResponse 객체에 응답을 보낸다.
      이 때, 각각의 Servlet 클래스는 JVM의 Class Loader에 의해 로딩된다. Java Servlet container는 서블릿 생성자를 호출하고, 각각의 서블릿 생성자는 어떠한 매개변수도 받지 않는다.

  • HTTP 요청 처리 과정을 보면 Servlet InstanceHTTP 요청이 올 때마다 기존의 Servlet Instance를 이용한다.
    즉, 하나의 Servlet Instance가 여러개의 HTTP 요청을 동시에 처리하게 된다. 따라서 Servlet InstanceThread-Safe하지 않다.
    • Thread-Safe한 변수를 이용하기 위해서는 Method의 지역변수를 이용해야 한다. Servlet Container는 사용되지 않아 제거되야 할 Servlet Instancedestory() 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이 필요없다는 얘기가 아니다.

출처

profile
백엔드를 공부하고 있습니다.

0개의 댓글