하지만, 웹 서버도 프로그램을 실행하는 기능을 포함하기도 하고, WAS도 정적 리소스등 웹 서버가 제공하는 기능들을 제공하기에, 둘의 경계가 모호하다.
WAS는 애플리케이션 코드를 실행하는데 더 특화 되어있다.
WAS가 웹 서버 기능까지 할 수 있다고 하더라도, WAS에게 너무 많은 역할을 담당하면 서버에 과부화가 되고, 정적 리소스 장애가 난다면, 애플리케이션 로직이 발생이 안된다.
그렇기 때문에, 정적 리소스는 웹 서버가 처리가 된다. 웹 서버는 정적 리소스를 내기 때문에, 에러가 날 위험이 적다. WAS는 애플리케이션 로직과 같은 동적 처리만 할 수 있도록 한다.
웹 서버와 WAS를 분리해두면, WAS에 오류가 발생해도 정적 리소스는 클라이언트한테 보낼 수 있다.
서블릿
웹 애플리케이션 서버를 직접 구현하면, 개발자가 개발해야 될 게 너무 많다.
의미 있는 로직 구현 외에 해야될 일이 너무 많다.
서블릿을 지원하는 WAS를 사용하면, 개발자는 의미있는 비즈니스 로직만 구현하면 된다.
그럼 서블릿은 뭔데?
동적 리소스를 만들 때 사용되는 자바 기반의 웹 애플리케이션 프로그래밍 기술이다. 웹 요청과 응답의 흐름을 간단한 메서드 호출만으로 체계적으로 다룰 수 있게 해주는 기술이다. 이 기술을 사용하면, 우리는 비즈니스 로직에만 집중하면 된다.
HTTP 스펙을 매우 편리하게 사용할 수 있다.
WAS는 Request, Response 객체를 새로 만들어서 서블릿 객체를 호출한다.
개발자는 Request 객체에서 HTTP 요청 정보를 편리하게 꺼내서 사용하면되고, Response 객체에서 HTTP 응답 정보를 편리하게 입력하면 된다.
WAS는 Response 객체에 담겨있는 내용으로 HTTP 응답정보를 생성하고, 브라우저에게 보내준다.
톰캣처럼 서블릿을 지원하는 WAS를 서블릿 컨테이너라고 한다.
서블릿 컨테이너는 서블릿 객체를 생성, 초기화, 호출, 종료하는 생명주기를 관리한다.
모든 서블릿 객체는 싱글톤으로 관리된다.
- 고객의 요청이 올 때 마다 계속 객체를 생성하는 것은 비효율적이다. 그래서 최초 로딩 시점에 서블릿 객체를 만들어두고 재활용을 한다. 모든 고객 요청시에 동일한 서블릿 객체 인스턴스에 접근한다. 그렇기 때문에 공유 변수를 사용에 주의를 해야한다.
동시 요청 - 멀티 쓰레드
쓰레드
애플리케이션 코드를 하나하나 순차적으로 실행하는 것은 쓰레드가 한다.
자바 메인 메서드를 처음 실행하면 main이라는 이름의 쓰레드가 실행된다.
쓰레드가 존재하지 않는다면, 자바 어플리케이션은 실행이 불가능하다.
쓰레드는 한번에 하나의 코드 라인만 수행한다.
동시 처리가 필요할 시에 쓰레드를 추가로 생성해서 사용할 수 있다.
다중 요청시 단일 쓰레드일 때 생기는 문제
단일 요청때 쓰레드를 하나만 사용한다면, 문제가 없다. 하지만, 다중 요청때 쓰레드를 하나만 사용된다면, 하나의 요청이 지연된다면, 다음 요청이 쓰레드를 할당받지 못하고, 계속 기다려야하는 문제가 생긴다.
해결 방법
요청이 들어 올 때마다 쓰레드를 생성하면, 이전 요청이 처리가 지연되더라도 다른 요청을 처리할 수 있다. → 쓰레드를 생성하는 비용은 매우 비싸다. 고객 요청이 올 때 마다 쓰레드를 생성한다면, 응답 속도가 늦어질 수 밖에 없다. → core는 한정적이다. 그래서 동시에 실행될 수 있는 쓰레드도 한정적이다. 중간에 다른 쓰레드를 사용하려면, 컨텍스트 스위칭이 필요하다. 무한정 쓰레드를 생성한다면, 켄텍스트 스위칭 비용이 늘어날 수 밖에 없다.(더 자주 바꿔야 되니까) → 쓰레드 생성에 제한이 없기 때문에, 너무 많은 요청이 오면, CPU, 메모리 임계점을 넘어서 서버가 죽을 수 있다.(최악의 상황임)
해결 방법의 해결 방법
쓰레드 풀을 생성한다.
필요한 쓰레드를 미리 생성하여, 쓰레드 풀에 보관하고 관리한다.
쓰레드 풀에 생성 가능한 쓰레드의 최대치를 관리한다. 톰캣은 최대 200개 기본 설정(변경 가능)
쓰레드가 필요하면, 새로 생성하는 것이 아닌, 이전에 만들어진 쓰레드를 쓰레드 풀에서 꺼내서 사용한다.
쓰레드 사용 종료되면, 쓰레드를 버리는 것이 아닌, 쓰레드 풀에 반납하여, 재사용 할 수 있도록 한다.
최대 쓰레드가 모두 사용중이어서 쓰레드 풀에 쓰레드가 없다면 기다리는 요청을 거절하거나 특정 숫자만큼만 대기하도록 설정할 수 있다.
쓰레드를 생성하고 종료하는 overhead를 절약하여, 응답시간이 빠르다.
너무 많은 요청이 들어와도, 기존의 요청을 받을 수 있는 쓰레드 수를 지정해놓았기 때문에, 서버가 과부하 걸려서 죽는 상황이 일어나지 않는다.