프로젝트 코드 리뷰 시간에, 한 캠퍼 분께서 Tomcat의 내부 구조를 설명해 주셨습니다. 특히, Acceptor Thread가 모든 요청을 받아 Worker Thread에 전달하고, Worker Thread에서 실제로 요청을 처리한다고 말씀하셨습니다. 당시에는 잘 몰랐지만 주말 동안 공부해보니 이는 바로 `Tomcat NIO Connector`에 대한 이야기였습니다.
이 이야기를 듣자마자 Tomcat 서버와 Spring WebFlux가 비슷하다는 느낌이 들었습니다. 그렇다면 Tomcat을 이용한 Spring Web MVC도 논블로킹이어야 할 것 같은데, 왜 우리는 여전히 one thread per request 동기 블로킹 서버로 알고 있을까요?
먼저 Tomcat 자체를 공부해보며 Servlet과 어떻게 연결되는지 살펴보고, 그 다음에 왜 Spring Web MVC가 동기-블로킹 서버인지를 알아보도록 하겠습니다.
Tomcat 서버를 구동했을 때 일어나는 일
첫 번째 단계에서는 ApplicationContext가 초기화되고 빈 등록이 완료됩니다.
두 번째 단계에서는 서버 객체를 만드는 단계입니다. 어떤 객체를 만들지는 `ServletWebServerFactory` 팩토리 클래스에서 결정하며, 결론적으로 Tomcat 서버 객체가 생성됩니다.
세 번째 단계는 Tomcat 서버를 시작하기 위한 준비 단계입니다. Connector는 Tomcat의 클라이언트 요청을 수신하는 구성 요소입니다. Tomcat은 다양한 커넥터를 지원하며, 이들은 각기 다른 프로토콜을 처리할 수 있습니다. Protocol은 Connector가 사용하는 프로토콜 구현체인데 `Http11NioProtocol`, `Http11BioProtocol` 등이 있습니다. 여기서 중요한 점은 현재의 Spring Boot는 Http11NioProtocol이 기본이며, Http11BioProtocol은 드랍되어 존재하지 않는다는 점입니다.
네 번째 단계는 진정한 의미에서의 서버 시작입니다. 서버 소켓을 열어 바인딩하고, 이제부터는 요청이 들어오길 기다리면 됩니다.
각 단계의 소스 코드는 다음과 같다.
1. refreshContext 어플리케이션 컨텍스트 초기화
SpringApplication을 구동하는데 필요한 모든 빈을 ApplicationContext에 등록하는 과정입니다.
Step2. WebServer 객체 생성 및 실행
내장 Tomcat 서버 객체를 생성하는 단계입니다. ServletWebServerApplicationContext의 onRefresh() 메소드를 통해 createWebServer()로 WebServer 객체를 생성합니다. 어떤 WebServer 객체를 만들지는 `TomcatServletWebServerFactory` 팩토리 클래스를 통해 결정됩니다.
TomcatServletWebServerFactory는 기본 프로토콜로 Http11NioProtocol을 사용하도록 합니다.
이제 Tomcat 서버를 구동시킵니다.
Step3. Connector 추가
Tomcat이 클라이언트의 요청을 수신할 수 있도록 커넥터를 추가합니다. 이때 추가되는 커넥터는 `Http11NioProtocol`을 처리할 수 있는 커넥터임이 중요합니다.
Step4. 서버 소켓 시작
지정한 포트의 서버 소켓이 생성됩니다. 8080 포트가 이미 존재하는지 확인하고 없으면 서버 소켓을 바인딩합니다. 그리고 `Acceptor 쓰레드`를 생성하여 실행시킵니다.
그리고 Acceptor 쓰레드를 생성하여 실행시킵니다.
마무리
이렇게 Tomcat의 논블로킹 I/O와 Spring Web MVC의 동기 블로킹 처리에 대해 살펴보았습니다. 다음에는 Tomcat NIO/BIO와 Netty에 대해 더 깊이 살펴보겠습니다.
참고
'☘️Spring' 카테고리의 다른 글
[Spring] Tomcat 2편. 톰캣 NIO 처리와 Spring MVC 가 블락킹인 이유 (0) | 2024.07.30 |
---|---|
[Spring] 스프링 시큐리티 인증/인가 및 에러 삽질 (3) | 2024.02.04 |
[Spring] 스프링 시큐리티 기초 정리 (1) | 2024.01.21 |
[Spring] JPA N+1 문제 해결 (35) | 2023.10.30 |
[Spring] MockMvcTest vs End-to-End Tests (1) | 2023.10.26 |