갑자기 스프링 시큐리티 다루다가 DispatcherServlet이 중앙에서 모든 HTTP 요청을 처리하고 적절한 핸들러에 전달해준다..이걸 보고..
그럼 접속하는 사용자가 엄청 많은데, 많은 요청이 갑자기 들어오게 된다면 이 많은 요청을 하나의 핸들러가 다 처리하는건가??
의문이 들어서 열심히 찾아봤다.
이런 답변을 보았다.
톰캣(WAS)은 하나의 프로세스에서 동작하며, thread pool을 만들어 HttpRequest가 들어왔을 때 하나씩 쓰레드를 재사용하거나 재배정한다. 요청이 많아지면, 큐에 쌓아두고 쓰레드가 만들어지는 일반적인 Thread pool 의 동작을 한다. |
본능적으로 생각했다..thread???는... 운영체제랑 객프 수업에서 들어봤는데,, 정확히 알지 못했다,,,
그래서 기초부터 하나하나 찾아봤다.
쓰레드와 프로세스
프로세스란?
일반적으로 CPU에 의해 메모리에 올려져 실행중인 프로그램이다. 자신만의 메모리 공간을 포함한 독립적인 실행 환경을 가지고 있다.
자바 JVM(자바 버츄얼 머신)은 주로 하나의 프로세스로 실행되고, 동시에 여러 작업을 수행하기 위해 멀티 쓰레드를 지원한다.
쓰레드란?
쓰레드는 프로세스 안에서 실질적으로 작업을 실행하는 단위를 말하고, 자바에서는 JVM에 의해 관리된다. 프로세스에는 적어도 한 개 이상의 쓰레드가 있고, Main 쓰레드 하나로 시작해서 쓰레드를 추가 생성하게 되면 멀티 쓰레드 환경이 된다.
그러면 좋은건가,, 하지만 쓰레드가 많으면 비용이 많이 들어간다.
CPU 오버헤드가 될 수도 있고, 메모리 누수가 생긴다.
그렇다면 어떻게 해결할 수 있을까?
그 방법이 바로 톰캣이 사용한다는 Thread pool이다!
Thread pool
쓰레드는 생성비용이 많이 들고, 너무 많이 만들면 위험한 리스크도 크다. 그래서 이를 해결하기 위해서 미리 만들어둔 쓰레드를 재사용할 수 있게 하는 것이 Thread pool이다!
만약에 요청이 쓰레드 수보다 많아지면, Queue에 대기하고 쌓이게 된다.
톰캣은?
톰캣은 하나의 프로세스로 실행되며, 이 하나의 프로세스 내에서 여러 개의 쓰레드가 동작하는 구조이다. 톰캣 프로세스가 멀티 쓰레드 환경에서 클라이언트의 여러 요청을 동시에 처리할 수 있는 이유가 thread pool을 이용한 구조이기 때문이다.
톰캣은 HTTP Request 수가 thread 수보다 증가하게 되면 큐에 대기시킨다. 톰캣은 일정한 크기의 큐를 유지하며, 요청은 처리할 스레드가 생성되거나 해제될 때까지 큐에서 대기하게 된다.
또, 톰캣은 maxThreads를 설정할 수가 있다. 이를 초과하면 더 이상 쓰레드를 생성하지 않으며, 그때는 추가 요청을 거부하거나 타임 아웃을 시킨다.
스프링부트의 동작을 먼저 이해하고 예를 들어보자.
스프링의 동작 방식
1. 웹 클라이언트는 사용자가 사용하는 브라우저나 애플리케이션이다. 클라이언트가 HTTP 요청을 보내면 서버가 HTTP 응답을 해주는 방식이다.
2. Servlet container 는 WAS(웹 애플리케이션 서버) 이다. WAS는 DB 조회 등 동적 로직을 처리한다. 클라이언트로부터 들어온 요청을 Servlet Container가 처리하고, 그 요청을 처리할 적절한 서블릿으로 넘겨준다.
3. 여기서 서블릿이란 JAVA 코드로 구현된 웹 컴포넌트로, 클라이언트의 요청을 처리하고 응답을 생성하는 역할을 한다.
그러면 스프링부트로 적용해보면,
Servlet container = 톰캣(WAS) 이며, 톰캣이 어느 서블릿이 요청을 담당해야 하는지 결정해준다.
Servlet = 웹 컴포넌트이다.
그 안의 Spring Container 는 스프링 프레임워크가 제공하는 핵심 컴포넌트 관리 시스템이다. 여기서 빈은 스프링이 관리하는 객체를 의미한다. 스프링은 싱글톤 패턴으로써 빈을 관리하며, 개발자가 아니라 스프링 컨테이너가 DI (의존성 주입)을 이용해 서로 연결 해준다.
- 이 빈들은 데이터베이스 접근, 서비스 로직 수행, 트랜잭션 관리 등의 역할을 담당한다.
위에서 서블릿이 요청을 처리하고 응답을 생성하는 역할을 한다고 했었다. 서블릿은 요청을 받고, 스프링 컨테이너와 상호작용을 해 필요한 빈(비즈니스 로직 등) 을 사용한다.
결론:
1. 톰캣(서블릿 컨테이너) 는 클라이언트로부터 들어온 요청을 수신하고, 이를 처리하기 위해 Thread pool에서 쓰레드를 할당한다.
2. Thread pool은 미리 생성된 쓰레드로 요청을 처리하고, 요청이 완료되면 쓰레드를 다시 풀로 반환한다.
3. 톰캣은 요청이 들어오면 DispatcherServlet이나 서블릿에 해당 요청을 전달하고, 서블릿은 스프링 컨테이너와 상호작용해 이에 필요한 빈을 사용해 요청을 처리한 후 응답을 생성한다.
4. 만약 요청이 많아지면 톰캣은 큐에 요청을 대기시켰다가 쓰레드가 준비되면 요청을 처리한다. 만약 요청이 너무 많아 큐가 가득 차면, 더 이상 요청을 처리할 수 없으므로 오류나 타임아웃을 발생시킨다.
그러면 /login api 로 로그인 요청을 보내면 하나의 logincontroller을 참고해야한다.
어떻게 많은 쓰레드들이 하나의 클래스를 참고할 수 있을까? 라는 의문이 들었다... 그래서 찾아봤다..^^
답은 Class 정보는 메소드 영역에 저장되기 때문에 모든 쓰레드가 객체의 Binary Code 정보를 공유할 수 있기 때문이다. |
쓰레드 공유 메모리???🤔 이를 이해하려면 자바의 메모리 구조부터 제대로 알고 넘어가야 했다 . .
자바의 메모리 구조
자바는 메소드, 힙, 스택 영역으로 이뤄져 있다.
메소드 영역 | static 영역이라고도 하며, 정적 멤버 변수와 전역 멤버변수가 저장되는 영역이다. | 자바에서는 클래스와 클래스 변수들이 저장된다. |
힙 영역 | 자바 프로그램에서 사용되는 모든 인스턴스 변수(객체)들이 저장되는 영역이며, 자바에서는 new를 사용해서 객체를 생성하면 힙 영역에 저장된다. 힙 영역은 메모리 공간이 동적으로 할당되고 해제되며, 메모리의 낮은 주소에서부터 높은 주소로 할당이 이뤄진다. | 자바에서는 객체 인스턴스 같은 것들이 저장된다. |
스택 영역 | 지역변수, 인자값, 리턴 값이 저장되는 영역이며 메소드 안에서 사용되는 기본적인 변수들과 값이 저장되는 영역이다. 힙 영역에서 생성된 객체들을 참조하는 주솟값이 할당된다. | 메소드 지역변수와 매개변수가 저장된다. |
쓰레드의 공유 메모리
멀티 쓰레드에서 메모리 영역은, 메소드 영역 + 힙 영역은 모든 스레드가 공유한다.
하지만, 스택의 경우에는 각 쓰레드 별로 하나씩 생성되어진다. 이러한 특징으로 각 쓰레드마다 스택 메모리는 다른 쓰레드에서 접근이 불가능하지만 메소드 영역과 힙 영역은 모든 쓰레드에서 접근이 가능하고 프로그램의 시작부터 종료까지 메모리에 남아 프로그램이 실행되고 있다면 어디서든지 사용이 가능하다.
➡️ 이것이 전역변수가 어디서든 접근 가능하고, Heap 영역의 객체를 주기적으로 삭제해야하는 이유가 된다.
'BACKEND > Spring Boot' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍 - 기본편] 1일 차 JPA에 대해서 (4) | 2024.10.21 |
---|---|
Spring Container는 어떻게 생성될까 (0) | 2024.09.22 |
테스트 스텁과 목 오브젝트의 차이점 (1) | 2024.09.19 |
JWT 에 대해서 (0) | 2023.11.07 |
스프링 시큐리티- UserDetails 관련 (0) | 2023.11.07 |
안녕하세오 저는 똑똑해지고 싶은 버그 수집가에오
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!