Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save taekwon-dev/41f9a55a3e6e0f9412d5aaff88b31436 to your computer and use it in GitHub Desktop.
Save taekwon-dev/41f9a55a3e6e0f9412d5aaff88b31436 to your computer and use it in GitHub Desktop.

How the Servlet Container Handles multithreading

Servlet, Servlet Container
How the Servlet Container Handles a request
How the Servlet Container Handles multithreading ◀︎ 현재 글
How to maintain a user state - HttpSession
Servlet Scopes

여러 클라이언트가 동시에 동일한 요청(예 - 좋아요 누른 포스트 리스트 조회)을 보내면 서블릿 컨테이너는 어떻게 처리할까?

image

image

위 질문에 대해서 직관적으로 [ 그림 1 ] 또는 [ 그림 2 ] 방식이 떠오를 수 있을 것 같다. [ 그림 1 ] 은 동일한 역할을 하는 서블릿 객체를 각 요청 별로 생성해서 처리 했고 [ 그림 2 ] 는 하나의 서블릿으로 모든 요청을 처리했다.

여기서 이전 글에서 다루기도 했던 서블릿 컨테이너가 서블릿 초기화하는 방법에 대해 상기할 필요가 있다. 서블릿 컨테이너는 기본적으로 최초의 클라이언트 요청이 왔을 때 대상 서블릿이 초기화되어 있지 않은 경우에 초기화를 한다. 즉, 이미 초기화가 되어 존재하는 경우에는 새롭게 초기화 (동적 생성) 하지 않고 기존 초기화 되어 있는 서블릿 인스턴스를 사용한다.

따라서 [ 그림 2 ] 가 서블릿 컨테이너 (Apache Tomcat)의 서블릿 운영 방식임을 알 수 있다. 이전에 사용했던 예를 가지고 한 번 더 살펴보자.

  1. 클라이언트가 (로그인 유저) 좋아요 누른 포스트 리스트를 조회 요청한다.
  2. 웹 서버는 이 요청을 서블릿 컨테이너 에 위임한다.
  3. 서블릿 컨테이너는 해당 요청을 처리할 수 있는 서블릿을 찾고, 위임한다.
  4. 서블릿은 해당 요청을 처리하고 동적 웹 페이지를 생성한다.
  5. 웹 서버는 서블릿을 통해 생성된 웹 페이지를 클라이언트에 반환한다.

3)에서 서블릿 컨테이너가 해당 요청을 처리할 수 있는 서블릿을 찾는 과정이 있는데, 이 과정이 초기화 된 서블릿 존재 여부를 확인해서 새롭게 대상 서블릿을 초기화 할 지 아니면 이미 초기화 된 서블릿 인스턴스를 사용할 지를 결정하게 된다.

그렇다면, 서블릿 컨테이너가 서블릿 찾을 수 있는 공간은 어디일까? 서블릿 인스턴스는 JVM 런타임 데이터 영역 중 힙(Heap) 영역에 저장된다. JVM 힙 영역은 동적으로 생성된 (new 키워드 등) 객체를 저장하는 공간으로 가비지 컬렉터의 관리 대상이 되는 공간이다. 따라서 서블릿 생명주기 중 destroy() 호출된 이후 서블릿 인스턴스 역시 가비지 컬렉터에 의해 처리된다. 또 다른 중요한 특징은 힙 영역은 스레드 별로 독립적인 공간이 아닌, 공유되는 공간이라는 점이다.

서블릿 컨테이너 (Apache Tomcat)은 동시에 오는 여러 요청을 처리할 때 요청 별로 스레드를 생성한다. 즉, 서블릿 컨테이너는 단일 서블릿을 사용하는 구조에서 동시에 오는 여러 요청을 처리하기 위해 멀티 스레드를 활용하는 것이다.

image

앞서 서블릿 인스턴스는 JVM의 힙 영역에 저장된다고 했다. 그리고 힙 영역의 중요한 특징으로 스레드 간 공유되는 공간이라는 점을 강조했다. 위 두 가지 사실을 통해서 우리는 서블릿이 Thread-safety 하지 않다는 것을 알 수 있다.

왜 그럴까? 단일 서블릿 + 멀티 스레드 방식으로 동시에 오는 여러 요청을 처리한다. 이 때 서블릿이 요청에 따른 특정한 상태를 갖는다고 가정할 때 문제가 발생한다. 클라이언트 A가 보낸 요청을 처리하는 과정에서 클라이언트 B가 보낸 요청에 의해서 서블릿 상태가 변경될 수 있고 따라서 클라이언트 A는 의도 했던 응답을 받지 못할 수 있다.


이번 글에서는 서블릿 컨테이너(Apache Tomcat)와 멀티 스레드를 활용하는 방식에 대해 다뤘다. 이 주제는 특히 JVM, Thread, Process + 서블릿 초기화 특징 등 여러 내용에 대한 이해가 어느정도 수반돼야 하는 만큼 공부하는 과정 그리고 글을 작성하는 과정이 쉽지 않았지만, 지금 시점도 분명 다음 단계로 도약하는 과정 중 한 시점이므로 좌절하지 않고 천천히 나아가고 싶다. 그리고 이 글을 읽으면서 같은 감정을 느낀 분들께 힘을 주고 싶다.

다음 글에서는 서블릿 컨테이너 내에서 유저(클라이언트)의 상태를 어떻게 관리하는 지 Session 을 중심으로 다룰 예정이다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment