스프링부트의 다중요청 처리

이지호·2021년 12월 6일
1

스프링부트

목록 보기
1/4
post-thumbnail

개인 프로젝트를 진행 하던 중 가끔씩 서버는 하나인데 어떻게 여러 요청에 대해서 처리를 하는 거지? 라는 생각이 종종 들때가 있었습니다.

이번에 그 궁금증에 대해서 알아보려고 합니다.

먼저, 클라이언트로부터 요청이 들어오면 스프링부트의 처리흐름은 다음과 같습니다.

그림에서는 안나와 있지만 클라이언트와 컨트롤러 사이에는 서블릿 컨테이너(Servlet Container)가 존재합니다.

클라이언트로부터 요청이 들어오면 서블릿 컨테이너가 요청에 알맞은 컨트롤러로 요청을 보내줄겁니다.

그럼 서블릿 컨테이너가 뭐냐?
간략하게 설명하면 서블릿의 라이프사이클을 관리하고, 서블릿 스레드를 생성하는 역할을 합니다.

스프링부트에서는 기본적으로 서블릿 컨테이너인 톰캣을 내장하고있습니다.

요청을 받는 곳을 정확히 말하자면 스프링부트가 아닌 서블릿 컨테이너에서 받는다고 말할 수 있겠습니다.

그러면 요청이 클라이언트로부터 요청이 들어오면 서블릿 컨테이너는 스레드를 생성하여 요청을 처리하게 됩니다.
스레들을 생성하는건 부하가 많은 작업입니다. 규모가 큰 서비스의 경우에 시스템 전체에 영향을 미쳐 사용자 경험이 매우 저하 될 것입니다.

이를 해결하기 위해 톰캣 3.2버전부터는 스레드 풀(Thread Pool)을 생성하여 요청을 처리하게 됩니다.

즉, 스프링부트의 다중요청 처리는 톰캣의 스레드 풀을 이용하여 처리한다고 생각하시면 될것 같습니다.

Connector

톰캣과 같은 서블릿 컨테이너는 Connector를 통해 외부와 통신을 합니다.
Connector는 Port를 listen하여 connection을 얻은 후 데이터 파싱을 통해 서블릿이 처리가능한 형태로 서블릿 컨테이너로 전송하는 역할을 담당합니다.

Connector의 종류는 BIO(Blocking IO) Connector와 NIO(Non-Blocking IO) Connector 2가지가 있는데 이번 글에서는 NIO만 간략하게 설명하겠습니다.

아래 그림은 톰캣의 버전별 Connector 스펙입니다.

NIO(Non-Blocking) Connector


NIO Connector는 Acceptor가 Connection을 허용한 후 Queue를 통해 Poller라는 별도의 스레드가 Connection을 처리하게 됩니다.

즉, Acceptor는 Producer의 역할을 하고, Poller는 Consumer의 역할을 하게 되는 것입니다.

다중요청 처리 테스트

예제코드
멀티모듈로 구성하여 요청모듈과 응답모듈 각각 하나씩 생성하였습니다.

Web Client를 이용하여 병렬요청을 실행하는 코드를 생성하였습니다.

해당 경로로 파라미터값을 입력하여 요청하면 입력한 수만큼 스레드를 생성하여 응답서버로 병렬 요청을 통해 순차대로가 아닌 응답을 받은 순서대로 출력을 하는 코드입니다.

요청을 받으면 RESPONSE_COUNT의 값을 증감시켜 응답을 해주는 코드입니다.

요청모듈과 응답모듈의 어플리케이션을 실행하고, 1000개의 요청을 동시 실행을 시켰습니다.

1000개의 요청을 보낸 후 요청서버의 스레드입니다. 사진에는 다 나오지는 않았지만 1000개의 스레드가 생성되었습니다.

응답서버의 스레드입니다. 1000개의 스레드가 생성되어 응답한게 아니라 톰캣이 필요한 만큼 생성하여 응답하였습니다.

정리

서블릿 컨테이너의 다중요청에 대한 처리방식과 VisualVM을 통해 스레드가 할당되는 모습을 확인하였습니다.
개인적으로 공부한 걸 새벽에 정리하려니 어떤걸 써야할지 모르겠고, 어수선하고 앞뒤가 잘 안맞는 거 같습니다.
이 글을 보고 조금이라도 도움이 되었다면 좋겠습니다.
수정할 사항이나 보충해야 할 부분이 있다면 언제든지 알려주시면 감사하겠습니다!!

0개의 댓글