서블릿은 어떻게 작동합니까? 인스턴스화, 세션, 공유 변수 및 멀티 스레딩


1144

수많은 서블릿을 보유하고있는 웹 서버가 있다고 가정 해보십시오. 해당 서블릿간에 정보를 전달하기 위해 세션 및 인스턴스 변수를 설정하고 있습니다.

이제 두 명 이상의 사용자가이 서버에 요청을 보내면 세션 변수는 어떻게됩니까?
모든 사용자에게 공통적이거나 각 사용자마다 다릅니 까?
서로 다른 경우 서버는 어떻게 다른 사용자를 구별 할 수 있었습니까?

하나 더 비슷한 질문, n특정 서블릿에 액세스하는 사용자가있는 경우이 서블릿은 처음 사용자가 처음 액세스 할 때만 인스턴스화되거나 모든 사용자에 대해 개별적으로 인스턴스화됩니까?
즉, 인스턴스 변수는 어떻게됩니까?

답변:


1821

서블릿 컨텍스트

서블릿 컨테이너 (예 : Apache Tomcat )가 시작되면 모든 웹 애플리케이션을 배치하고로드합니다. 웹 애플리케이션이로드되면 서블릿 컨테이너는 ServletContext한 번 작성 하여 서버의 메모리에 유지합니다. 웹 응용 프로그램의 web.xml및 포함 된 모든 web-fragment.xml파일을 구문 분석하고, 각되고 <servlet>, <filter><listener>발견 (또는 각 클래스는 주석 @WebServlet, @WebFilter@WebListener각각) 한 번 인스턴스화하고 서버의 메모리에 저장도된다. 인스턴스화 된 각 필터에 대해 해당 init()메소드는 new로 호출됩니다 FilterConfig.

A는 때 Servlet<servlet><load-on-startup>@WebServlet(loadOnStartup)보다 더 가치 이상을 0, 다음의 init()방법은 새로운으로 시작하는 동안 호출됩니다 ServletConfig. 해당 서블릿은 해당 값으로 지정된 순서대로 ( 11 일, 22 일 등) 초기화됩니다 . 동일한 값 이상의 서블릿 지정되면 그들은에 나타나는 경우, 그 다른 서블릿 각각 동일한 순서로 로딩 web.xml, web-fragment.xml또는 @WebServlet클래스로드. "시작시로드"값이 없으면 init()HTTP 요청이 해당 서블릿을 처음으로 칠 때마다 메소드가 호출됩니다.

서블릿 컨테이너가 위에서 설명한 모든 초기화 단계로 완료 ServletContextListener#contextInitialized()되면이 호출이 호출됩니다.

아래 서블릿 컨테이너 닫힌다, 그것은 모든 웹 응용 프로그램을 언로드 할 때, 호출 destroy()의 초기화 서블릿과 필터, 모든 모든 방법을 ServletContext, Servlet, Filter그리고 Listener인스턴스는 휴지통된다. 마지막으로 ServletContextListener#contextDestroyed()호출됩니다.

HttpServletRequest 및 HttpServletResponse

서블릿 컨테이너는 특정 포트 번호에서 HTTP 요청을 수신하는 웹 서버에 연결됩니다 (포트 8080은 일반적으로 개발 중에 사용되고 포트 80은 프로덕션에 사용됨). 클라이언트 (웹 브라우저, 또는으로 예를 들어 사용자의 경우 프로그램 사용이URLConnection ) HTTP 요청을 전송, 서블릿 컨테이너는 새로운 생성 HttpServletRequestHttpServletResponse객체와 정의 된 모든 통해 전달 Filter, 결국, 체인 및 Servlet인스턴스를.

의 경우 필터doFilter()메서드가 호출됩니다. 서블릿 컨테이너의 코드가를 호출 chain.doFilter(request, response)하면 요청 및 응답이 다음 필터로 계속 진행되거나 남아있는 필터가없는 경우 서블릿에 도달합니다.

의 경우 서블릿service()메서드가 호출됩니다. 기본적으로이 메소드는의 doXxx()기반으로 호출 할 메소드 중 하나를 결정 request.getMethod()합니다. 결정된 메소드가 서블릿에 없으면 응답에 HTTP 405 오류가 리턴됩니다.

요청 객체는 URL, 헤더, 쿼리 문자열 및 본문과 같은 HTTP 요청에 대한 모든 정보에 대한 액세스를 제공합니다. 응답 객체는 원하는 방식으로 HTTP 응답을 제어하고 보낼 수있는 기능을 제공합니다. 예를 들어 헤더 및 본문 (일반적으로 JSP 파일에서 생성 된 HTML 내용으로)을 설정할 수 있습니다. HTTP 응답이 커밋되고 완료되면 요청 및 응답 개체가 모두 재활용되고 재사용 할 수있게됩니다.

HttpSession

클라이언트가 처음으로 webapp를 방문하거나 HttpSession를 통해 처음으로 얻은 request.getSession()경우, 서블릿 컨테이너는 새 HttpSession객체를 생성하고 길고 고유 한 ID (생성 할 수 있음 session.getId())를 생성하여 서버에 저장합니다. 기억. 서블릿 컨테이너는 또한 설정 Cookie에서 Set-Cookie와 HTTP 응답의 헤더 JSESSIONID의 이름과 그 값과 고유 세션 ID있다.

당으로 HTTP 쿠키 사양 (계약을 준수해야 어떤 점잖은 웹 브라우저와 웹 서버), 클라이언트 (웹 브라우저)는 후속 요청에서이 쿠키 다시 보낼 필요가 Cookie긴 쿠키가 유효한 경우로에 대한 헤더 ( 즉, 고유 ID는 만료되지 않은 세션을 참조해야하며 도메인과 경로는 정확합니다). 브라우저의 내장 HTTP 트래픽 모니터를 사용하여 쿠키가 유효한지 확인할 수 있습니다 (Chrome / Firefox 23 + / IE9 +에서 F12를 누르고 Net / Network 탭을 확인 하십시오). 서블릿 컨테이너는 Cookie들어오는 HTTP 요청 의 헤더에 이름이있는 쿠키 가 있는지 확인하고 JSESSIONID해당 값 (세션 ID)을 사용하여 HttpSession서버 메모리에서 관련 항목을 가져옵니다 .

의 설정에 HttpSession지정된 시간 초과 값을 초과하여 유휴 상태가 될 때까지 (즉, 요청에 사용되지 않음) 유지 <session-timeout>됩니다 web.xml. 시간 종료 값은 기본적으로 30 분입니다. 따라서 클라이언트가 지정된 시간보다 오랫동안 웹 앱을 방문하지 않으면 서블릿 컨테이너가 세션을 휴지통에 버립니다. 쿠키가 지정된 경우에도 이후의 모든 요청은 더 이상 동일한 세션에 액세스 할 수 없습니다. 서블릿 컨테이너는 새 세션을 만듭니다.

클라이언트 측에서 세션 쿠키는 브라우저 인스턴스가 실행되는 동안 활성 상태를 유지합니다. 따라서 클라이언트가 브라우저 인스턴스 (모든 탭 / 창)를 닫으면 세션이 클라이언트 쪽에서 휴지통으로 이동합니다. 새 브라우저 인스턴스에는 세션과 관련된 쿠키가 존재하지 않으므로 더 이상 쿠키가 전송되지 않습니다. 이로 인해 HttpSession완전히 새로운 세션 쿠키가 사용되면서 완전히 새로운 것이 생성됩니다.

간단히 말해서

  • ServletContext한 웹 응용 프로그램의 삶 등을위한 삶. 모든 세션의 모든 요청 간에 공유 됩니다.
  • HttpSession클라이언트가 동일한 브라우저 인스턴스를 사용하여 웹 앱과 상호 작용하고 서버 측에서 세션 시간이 초과되지 않는 한 수명이 지속됩니다. 동일한 세션의 모든 요청 간에 공유 됩니다.
  • HttpServletRequestHttpServletResponse서블릿이 클라이언트에서 HTTP 요청을 수신 할 때부터 라이브, 전체 응답 (웹 페이지)에 도착 할 때까지. 그것은되어 있지 다른 곳에서 공유했다.
  • 모든 Servlet, Filter그리고 Listener인스턴스는 한 웹 응용 프로그램이 삶으로 살고 있습니다. 그들은 공유됩니다 모두 에서 요청 하는 모든 세션.
  • 모든 attribute정의되는 ServletContext, HttpServletRequest그리고 HttpSession질문의 삶에서 대상으로 한 살 것이다. 객체 자체는 JSF, CDI, Spring 등과 같은 Bean 관리 프레임 워크에서 "범위"를 나타냅니다. 이러한 프레임 워크는 범위가 지정된 Bean을 attribute가장 가까운 일치 범위로 저장합니다.

스레드 안전

즉, 주요 관심사는 스레드 안전성 일 수 있습니다. 서블릿과 필터가 모든 요청에서 공유됨을 알아야합니다. Java의 좋은 점은 멀티 스레드이며 다른 스레드 (읽기 : HTTP 요청)가 동일한 인스턴스를 사용할 수 있다는 것입니다. 그렇지 않으면 다시 너무 비싸, 것입니다 init()destroy()모든 단일 요청에 대해 그들.

또한 요청 또는 세션 범위 데이터를 서블릿 또는 필터 의 인스턴스 변수 로 지정 해서는 안된다는 것을 알아야합니다 . 다른 세션의 다른 모든 요청과 공유됩니다. 그건 하지 스레드 안전! 아래 예제는 이것을 보여줍니다.

public class ExampleServlet extends HttpServlet {

    private Object thisIsNOTThreadSafe;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object thisIsThreadSafe;

        thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
        thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
    } 
}

또한보십시오:


25
그래서 어떻게 든 클라이언트에게 보내는 JSessionId를 찾을 수있을 때 세션을 훔칠 수 있습니까?
Toskan

54
@Toskan : 맞습니다. 그것은으로 알려져 세션 고정 해킹 . 이것은 JSP / Servlet에만 국한되지 않습니다. 쿠키를 사용하여 세션을 유지 관리하는 다른 모든 서버 측 언어는 쿠키가 포함 된 PHP, PHPSESSID쿠키가 포함 된 ASP.NET 등과 같이 민감 ASP.NET_SessionID합니다. 그렇기 ;jsessionid=xxx때문에 일부 JSP / Servlet MVC 프레임 워크가 자동으로 수행하는 URL 재 작성이 어려워지는 이유도 있습니다 . 알지 못하는 최종 사용자가 공격받지 않도록 세션 ID가 URL이나 웹 페이지에 노출되지 않도록하십시오.
BalusC

11
@Toskan : 또한 웹앱이 XSS 공격에 민감하지 않은지 확인하십시오. 즉, 이스케이프되지 않은 형식으로 사용자 제어 입력을 다시 표시하지 않습니다. XSS는 모든 최종 사용자의 세션 ID를 수집 할 수있는 방법을 제공합니다. XSS의 기본 개념은 무엇입니까?를
BalusC

2
@BalusC, 내 멍청한 죄송합니다. 모든 사용자가 동일한 인스턴스에 액세스 할 수 있음을 의미합니다.
압도

4
전체 서블릿 자체가 없으면 @TwoThumbSticks 404가 리턴됩니다. 서블릿이 있지만 원하는 doXxx () 메소드가 구현되지 않은 경우 405가 리턴됩니다.
BalusC

428

세션

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

즉, 웹 서버 는 방문시 각 방문자 에게 고유 식별자를 발급합니다 . 방문자는 다음 번에 자신을 인식 할 수 있도록 해당 ID를 가져와야합니다. 이 식별자를 사용하면 서버가 한 세션이 소유 한 개체를 다른 세션의 개체와 올바르게 분리 할 수 ​​있습니다.

서블릿 인스턴스화

만약 시작시로드가 있습니다 거짓 :

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

만약 시작시로드가 있다 진실 :

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

그가 서비스 모드와 그루브에 있으면 동일한 서블릿이 다른 모든 클라이언트의 요청에 대해 작동합니다.

여기에 이미지 설명을 입력하십시오

클라이언트 당 하나의 인스턴스를 갖는 것이 좋지 않은 이유는 무엇입니까? 생각해보십시오 : 주문할 때마다 피자 한 명을 고용 하시겠습니까? 그렇게하면 곧 비즈니스가 중단 될 것입니다.

그러나 작은 위험이 따릅니다. 기억하십시오 :이 싱글 녀석은 주머니에 모든 주문 정보를 보유하고 있습니다 . 서블릿의 스레드 안전에 대해주의하지 않으면 특정 클라이언트에게 잘못된 주문을 줄 수 있습니다.


26
당신의 사진은 제 이해력에 아주 좋습니다. 하나의 질문이 있습니다. 피자 주문이 너무 많을 때이 피자 식당에서 무엇을합니까? 피자 한 사람을 기다리거나 더 많은 피자 녀석을 고용합니까? 감사 .
zh18

6
그는 다음과 같은 메시지를 반환합니다to many requests at this moment. try again later
Please_Dont_Bully_Me_SO_Lords

3
서블릿은 피자 배달원과 달리 동시에 둘 이상의 배달을 할 수 있습니다. 그들은 고객의 주소, 피자의 맛을 적는 곳에 특별한주의를 기울여야합니다.
bruno

42

Java 서블릿의 세션은 PHP와 같은 다른 언어의 세션과 동일합니다. 사용자에게 고유합니다. 서버는 쿠키, URL 재 작성 등과 같은 다양한 방법으로 서버를 추적 할 수 있습니다.이 Java doc 기사는 Java 서블릿의 컨텍스트에서이를 설명하고 세션이 유지 보수되는 방법이 서버 설계자에게 남겨진 구현 세부 사항임을 정확하게 나타냅니다. 이 사양은 서버에 대한 여러 연결을 통해 사용자에게 고유하게 유지되어야한다고 규정합니다. 두 가지 질문에 대한 자세한 내용은 Oracle의이 기사를 확인하십시오 .

편집 여기 서블릿 내부에서 세션을 다루는 방법에 대한 훌륭한 튜토리얼이 있습니다 . 그리고 여기에 그들이 및 사용 방법에 어떤 자바 서블릿에 대한 썬의 장이다. 이 두 기사 사이에서 모든 질문에 대답 할 수 있어야합니다.


전체 응용 프로그램에 대한 서블릿 컨텍스트가 하나뿐 이므로이 서블릿 컨텍스트를 통해 세션 변수에 액세스 할 수 있으므로 세션 변수가 모든 사용자에게 고유 할 수 있습니까?
Ku Jon

1
servletContext에서 세션에 어떻게 액세스하고 있습니까? servletContext.setAttribute ()를 언급하고 있지 않습니까?
matt b

4
@ KuJon 각 웹 응용 프로그램에는 하나의 ServletContext개체가 있습니다. 해당 객체에는 세션 객체 모음 인 0 개 또는 하나 이상의 세션 객체가 있습니다. 각 세션은 다른 답변의 만화에서 볼 수 있듯이 일종의 식별자 문자열로 식별됩니다. 이 식별자는 쿠키 또는 URL 재 작성을 통해 클라이언트에서 추적됩니다. 각 세션 개체에는 고유 한 변수가 있습니다.
Basil Bourque

33

서블릿 컨테이너 (Apache Tomcat과 같은)가 시작되면 컨테이너 측 콘솔에 문제가 발생하거나 오류가 표시되면 web.xml 파일 (애플리케이션 당 하나만)에서 읽습니다. 그렇지 않으면 모든 웹을 배포하고로드합니다 web.xml을 사용하여 응용 프로그램 (배포 설명 자라고도 함)

서블릿의 인스턴스화 단계에서 서블릿 인스턴스는 준비되었지만 두 가지 정보가 누락되어 클라이언트 요청을 처리 할 수 ​​없습니다.
1 : 컨텍스트 정보
2 : 초기 구성 정보

서블릿 엔진은 위에 누락 된 정보를 캡슐화하는 servletConfig 인터페이스 오브젝트를 작성합니다. 서블릿 엔진은 servletConfig 오브젝트 참조를 인수로 제공하여 서블릿의 init ()를 호출합니다. init ()가 완전히 실행되면 서블릿은 클라이언트 요청을 처리 할 준비가됩니다.

Q) 서블릿의 수명 동안 몇 번이나 인스턴스화 및 초기화가 발생합니까 ??

A) 한 번만 (모든 클라이언트 요청에 대해 새 스레드가 작성 됨) 한 개의 서블릿 인스턴스 만 클라이언트 요청을 처리합니다. 다른 클라이언트 요청을 기다립니다. 즉, 모든 클라이언트 요청에 대해 새 프로세스가 작성되는 CGI 제한이 서블릿 (내부 서블릿 엔진이 스레드를 작성 함)으로 극복합니다.

Q) 세션 개념은 어떻게 작동합니까?

A) getSession ()이 HttpServletRequest 객체에서 호출 될 때마다

1 단계 : 요청 세션이 들어오는 세션 ID에 대해 평가됩니다.

2 단계 : ID를 사용할 수없는 경우 새 HttpSession 오브젝트가 작성되고 해당 세션 ID가 생성되면 (예 : HashTable) 세션 ID가 httpservlet 응답 오브젝트에 저장되고 HttpSession 오브젝트의 참조가 서블릿 (doGet / doPost)으로 리턴됩니다. .

3 단계 : 사용 가능한 ID 새 세션 오브젝트가 작성되지 않은 경우 세션 ID를 키로 사용하여 세션 콜렉션에서 요청 오브젝트 검색이 수행됩니다.

검색에 성공하면 세션 ID가 HttpServletResponse에 저장되고 기존 세션 오브젝트 참조가 UserDefineservlet의 doGet () 또는 doPost ()로 리턴됩니다.

노트 :

1) 제어가 서블릿 코드에서 클라이언트로 떠날 때 세션 개체가 서블릿 컨테이너, 즉 서블릿 엔진에 의해 유지되고 있음을 잊지 않습니다

2) 멀티 스레딩은 구현을 위해 서블릿 개발자들에게 있습니다. 즉, 멀티 스레드 코드에 대해 신경 쓰지 않아도 클라이언트의 여러 요청을 처리하지 않습니다.

짧은 형태 :

서블릿은 응용 프로그램이 시작될 때 (서블릿 컨테이너에 배포 됨) 또는 서블릿이 인스턴스화 될 때 처음 시작될 때 (시작시 설정에 따라) 시작될 때 생성됩니다. 서블릿의 init () 메소드가 호출됩니다. 그런 다음 서블릿 (하나의 유일한 인스턴스)은 모든 요청 (여러 스레드가 호출하는 service () 메소드)을 처리합니다. 그렇기 때문에 동기화가 바람직하지 않으며 애플리케이션이 배포 취소 될 때 (서블릿 컨테이너가 중지 된 경우) destroy () 메소드가 호출 될 때 서블릿의 인스턴스 변수를 피해야합니다.


20

세션 -크리스 톰슨의 말.

인스턴스화 -서블릿은 컨테이너가 서블릿에 맵핑 된 첫 번째 요청을 수신 할 때 인스턴스화됩니다 (서블릿이 시작시 <load-on-startup>요소를 사용하여로드하도록 구성되지 않은 경우 web.xml). 후속 요청을 처리하는 데 동일한 인스턴스가 사용됩니다.


3
옳은. 추가 생각 : 각 요청은 해당 단일 서블릿 인스턴스에서 실행할 새로운 (또는 재활용 된) 스레드를 얻습니다. 각 서블릿에는 하나의 인스턴스와 많은 스레드가있을 수 있습니다 (동시 요청이 많은 경우).
Basil Bourque

13

서블릿 사양 JSR-315 는 서비스 (및 doGet, doPost, doPut 등) 메소드 (2.3.3.1 멀티 스레딩 문제, 페이지 9)에서 웹 컨테이너 동작을 명확하게 정의합니다.

서블릿 컨테이너는 서블릿의 서비스 메소드를 통해 동시 요청을 보낼 수 있습니다. 요청을 처리하려면 서블릿 개발자는 서비스 메소드에서 여러 스레드를 사용한 동시 처리를위한 적절한 프로비저닝을 수행해야합니다.

권장되지는 않지만 개발자의 대안은 서비스 메소드에 한 번에 하나의 요청 스레드 만 있음을 보장하기 위해 컨테이너가 필요한 SingleThreadModel 인터페이스를 구현하는 것입니다. 서블릿 컨테이너는 서블릿에서 요청을 직렬화하거나 서블릿 인스턴스 풀을 유지함으로써이 요구 사항을 충족시킬 수 있습니다. 서블릿이 분배 가능으로 표시된 웹 애플리케이션의 일부인 경우, 컨테이너는 애플리케이션이 분배되는 각 JVM에서 서블릿 인스턴스 풀을 유지할 수 있습니다.

SingleThreadModel 인터페이스를 구현하지 않는 서블릿의 경우 서비스 메소드 (또는 HttpServlet 추상 클래스의 서비스 메소드에 전달 된 doGet 또는 doPost와 같은 메소드)가 동기화 된 키워드로 정의 된 경우, 서블릿 컨테이너는 인스턴스 풀 접근을 사용할 수 없습니다. 요청을 직렬화해야합니다. 이러한 상황에서 개발자는 성능에 부정적인 영향을 미치므로 서비스 방법 (또는 전달 된 방법)을 동기화하지 않는 것이 좋습니다.


2
참고로, 현재 서블릿 스펙 (2015-01)은 3.1이며 JSR 340에 의해 정의됩니다 .
Basil Bourque


1
매우 깔끔한 답변! @tharindu_DG
Tom Taylor

0

위의 설명에서 알 수 있듯이 SingleThreadModel 을 구현 하면 서블릿 컨테이너가 서블릿을 스레드로부터 안전하게 보호 할 수 있습니다. 컨테이너 구현은 두 가지 방법으로이를 수행 할 수 있습니다.

1) 단일 인스턴스로 요청을 직렬화 (큐잉)-이는 서비스 / doXXX 메소드를 동기화하는 SingleThreadModel BUT을 구현하지 않는 서블릿과 유사합니다. 또는

2) 인스턴스 풀 생성-서블릿을 호스팅하는 환경의 제한 매개 변수 (메모리 / CPU 시간)와 비교하여 서블릿의 부팅 / 초기화 노력 / 시간 사이의 트레이드 오프와 더 나은 옵션입니다.


-1

서블릿은되어 스레드로부터 안전하지 않습니다

한 번에 두 개 이상의 스레드에 액세스 할 수 있습니다.

U를 Servlet을 Thread safe로 만들고 싶다면 U는

Implement SingleThreadInterface(i) 이것은 비어있는 인터페이스입니다.

행동 양식

또는 우리는 동기화 방법으로 갈 수 있습니다

우리는 동기화를 사용하여 전체 서비스 방법을 동기화 된 것으로 만들 수 있습니다

메소드 앞의 키워드

예::

public Synchronized class service(ServletRequest request,ServletResponse response)throws ServletException,IOException

또는 코드의 블록을 동기화 블록에 넣을 수 있습니다

예::

Synchronized(Object)

{

----Instructions-----

}

전체 블록을 만드는 것보다 동기화 된 블록이 더 좋다고 생각합니다.

동기화

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.