Spring Project를 하면서 정작 Http 요청이 들어왔을 때, Tomcat이 어떻게 동작되고, 요청들이 어떻게 처리가 되는지 몰랐던 부분이 많았습니다. 매번 요청이 들어오면 Controller에 맵핑 되서 값을 반환하는거 아니야? 정도만 알았던 저를 반성하면서 이 글을 적게됩니다.
먼저 위의 그림을 보면서 딱 떠오르는게 뭐가 있나요? 저는 Client가 Servlet까지 가는데 Connector를 거쳐가는게 보입니다. 말 그대로 Connector는 Client의 요청을 처리해 서버(Servlet Container)로 넘겨주는 역할을 합니다. 그러면 Connector에 대해서 자세하게 알아볼까요?
Socket을 연결하고 데이터 패킷을 얻어 ServletRequest 객체를 생성하여 Servlet Container에게 전달해주는 역할
Connector에는 2가지 방식이 있습니다. Bio Connector, Nio Connector가 있는데, 그 중 Nio Conncetor에 대해 좀 더 자세하게 다루려고 합니다.
Bio Connector 는 서버가 Client의 요청을 받을 때마다 새로운 Thread를 생성하여 처리하는 방식입니다. 만약 다수의 Client가 서버에 요청을 보내게 되면 어떻게 될까요? 대기열에 계속 쌓으면서 처리를 하다보니 idle 상태로 낭비되는 시간이 많아져 효율적으로 자원을 처리하지 못하게 됩니다. 따라서 이를 해결하기 위해 Nio Connector 가 등장하게 됩니다.
서버가 Client의 요청을 받을 때, 하나의 Thread가 여러 Client의 요청을 이벤트 기반(Selector)으로 처리하는 방식입니다.
1. Acceptor는 Port Listener를 통해 Socket Connection을 Accept
2. Socket에서 Nio Channel 객체를 얻어 NioSocketWrapper를 Poller Event 객체로 캡슐화하고 PollerEvent Queue에 삽입
3. Poller Thread는 Selector.select()를 호출하여 데이터를 읽을 준비가 된 Socket을 감지
4. 데이터를 읽을 수 있는 Socket이 감지되면 select() return 값을 반환하여 해당 Channel 가져옴
5. 해당 Channel을 처리할 Worker Thread를 Thread Pool에서 할당하여 처리
6. Worker Thread가 Poller에 의해 Socket을 넘겨받게되면, Socket을 SocketObjectProcessor로 캡슐화 함
7. Socket Processor에서 여러 Step을 거쳐 ApplicationFilterChain까지 도달
8. 이후 Servlet.service()를 호출해 ServletRequest, ServletResponse를 Servlet에 전달
❗️ Thread : CPU에 작업요청을 하는 실행단위
❗️ Thread Pool : 작업이 들어올 때마다 미리 만들어져 있는 Thread 들 중 하나에 작업할당
결론적으로 Http의 요청은 ServletRequest, ServletResponse 객체에 담아 Servlet Container에 있는 Servlet에 전달합니다. 이후의 흐름에 대해서는 다음 게시글에 적어보도록 하겠습니다. 이제부터는 저희가 잘 아는 Spring의 MVC 패턴이 나오지 않을까 싶네요.