본문 바로가기

서버/SpringBoot

스프링부트가 다중 유저 요청을 처리하는 방법 (Thread Pool, Thread Safe)

스프링 요청 처리 과정

내장 서블릿 컨테이너인 Tomcat을 이용한다.

  1. Tomcat은 다중 용청을 처리하기 위해서 부팅할 때 스레드 컬렉션인 Thread Pool을 생성한다.
  2. 유저 요청이 들어온다 (HttpServletRequest)
  3. 스레드 풀에서 하나씩 Thread를 할당함
  4. 해당 Thread에서 스프링 부트에서 작성한 dispatcher servlet을 거쳐 유저 요청을 처리한다.
  5. 작업을 끝내고 나면 스레드는 스레드풀로 반환된다.

application.yml 에서 아래처럼 톰캣설정을 바꿔줄 수 있다.

# application.yml (적어놓은 값은 default)
server:
  tomcat:
    threads:
      max: 200 # 생성할 수 있는 thread의 총 개수
      min-spare: 10 # 항상 활성화 되어있는(idle) thread의 개수
    max-connections: 8192 # 수립가능한 connection의 총 개수
    accept-count: 100 # 작업큐의 사이즈
    connection-timeout: 20000 # timeout 판단 기준 시간, 20초
  port: 8080 # 서버를 띄울 포트번호

스레드풀 설정

스레드풀이란

프로그램 실행에 필요한 Thread들을 미리 생성해놓는다는 개념이다. (스레드는 cpu의 자원을 이용해서 코드를 실행하는 하나의 단윙)

tomcat 3.2 이전 버전에서는 스레드풀을 만들어 놓지 않고 유저 요청이 들어올 때마다 servlet을 실행할 스레드를 하나씩 생성하고 요청이 끝나면 제거했다. → 스레드를 생성하고 소멸할 때 os와 jvm에 부담이 된다는 문제가 있었음.

1 스레드 = 1 사용자 는 아니다!

Connector

Connector는 소켓 연결을 수입하고 데이터 패킷을 획득하여 HttpServletRequest 객체로 변환하고, 서블렛 객체에 전달하는 역할을 함

BIO Connector

스레드 풀에 의해 관리되는 스레드는 소켓 연결을 받고 요청을 처리하고 응답한 후 소켓 연결이 종료되면 풀에 다시 돌아오게 된다.

connection이 닫힐 때까지 하나의 스레드는 특정 커넥션에 계속 할당되어 있음. 동시에 사용되는 스레드 수 = 동시 접속할 수 있는 사용자 수 가됨. → 스레드 충분히 사용X, idle 상태로 낭비되는 시간이 많음 → 문제 해결을 위해 NIO 등장

NIO Connector

NIO Connector에선 Poller라고 하는 별도의 스레드가 커넥션을 처리함. Poller는 소켓들을 캐시로 들고 있다가 해당 소켓에서 데이터에 대한 처리가 가능한 순간에만 스레드를 할당하는 방식을 사용해서 스레드가 idle 상태로 낭비되는 시간을 줄여준다.

NIO 기반 Connector는 하나의 Connection이 하나의 스레드를 할당받는 BIO Connector에 비해 Selector를 호라용해서 소켓을 관리하므로 더 적은 스레드를 사용한다. 또한 max-connections값까지 접속을 유지하고 스레드가 모자라면 맥스 사이즈까지 스레드를 추가한다.

⚠️ 톰캣 8.0부터 NIO(NonBlocking I/O) Connector이 기본으로 채택됨

Singleton

싱글톤이 아니라면 여러 사용자들로 인해 발생하는 요청을, 멀티 스레드로 생성된 여러 스레드가 처리하는 과정마다 필요한 객체를 생성해야 할 것이다.

스프링에서 모든 Bean을 싱글톤 객체로 생성해서 모든 사용자들의 스레드가 공유할 수 있도록 만든것이다.

Thread Safe

멀티 스레드 프로그래밍에서 어떤 공유자원에 여러 스레드가 동시에 접근해도 프로그램 실행에 문제가 없는 상태

공유자원에 동시에 여러 스레드가 접근해서 호출해도, 각 스레드에서 결과가 올바르게 나오는 상태

spring bean은 thread-safe 할까?

singleton scope로 생성된 bean들은 Thread Safe 할까?? Spring Container내에서 하나만 존재하도록 보장하지만 그것이 Thread-Safe 를 말하는 것은 아니며, 이러한 부분은 오히려 개발자가 직접 핸들링해줘야한다.

jvm에서 각각의 스레드는 자신의 stack영역을 가지고 있지만 heap영역은 스레드 간에 공유하고 있다.

객체가 읽기 전용이면 상관없지만, 상태를 변경할 수 있게 만든다면 thread-safe하지 않으며 이런 부분에 대해서는 인지하고 개발해야 한다.

참고

반응형