Node.js의 작업 처리방식
Node.js
애플리케이션은 단일 Thread 이벤트 기반 모델로 실행된다. Node.js
는 백그라운드에서 Thread 풀을 구성해 작업을 수행하지만, 애플리케이션 자체에는 Multi-Thread 개념이 없다. 그렇다면 어떻게 많은 사용자의 요청을 짧은 시간에 처리할 수 있을까? 정답은 Event Callback 방식이다.
Event Callback 방식과 Thread 방식의 비교
Thread 기반 웹 모델에서는 요청이 웹 서버로 도착하면 현재 Thread 풀에 가용한 Thread에게 그 작업을 할당한다(Multi-Thread). 그리고 해당 요청에 대한 처리는 동일한 Thread에서 지속된다. 예를 들어, File을 받아오는 작업과 Data를 받아오는 작업이 있다고 하면 2개의 작업을 처리하기 위해 2개의 Thread가 생성이 되고 각각의 Thread에서 File을 받아오는 작업과 Data를 받아오는 작업을 처리하게 된다.
이러한 방식에는 몇 가지의 문제점이 존재한다. 첫 번째 문제는, 가용한 Thread의 수에 한계가 있다는 것이다. 한 서버에서 가용한 Thread가 개수가 10개라고 제한한다면 11번째로 작업이 들어온 경우에 기존에 작업을 처리하고 있는 Thread 중 하나의 작업이 끝나야 그 다음 작업을 처리할 수 있다. 두 번째 문제는 Thread들이 공유하는 CPU의 제한이다. 하나의 Thread에서 CPU를 사용하고 있으면 해당 작업이 끝날 때까지 다른 Thread들이 모두 기다려야 한다는 것이다.
이에 비해, Node.js
는 작업을 처리하는데 Event Callback 방식을 사용한다. Event Callback 방식은 단일 Thread를 사용하여 이벤트 루프를 돌고, 이벤트 큐에 추가되어 있는 작업들을 순서대로 Thread에 할당해서 처리하는 방식을 사용한다. 단일 Thread를 사용하기 때문에 Thread 수에 제약을 받지 않고 하나의 Thread에서 하나의 CPU를 사용하기 때문에 CPU의 사용에 대한 기다림이 없는 것이다.
작업들 중에서 오랜 시간이 걸리거나 Block이 있는 작업들은 이벤트 루프에서 직접 처리하지 않고 콜백 함수와 함께 이벤트 큐에 추가한다. 그리고 그 다음 실행할 작업을 이벤트 큐에 추가한다. 이 작업들은 실행할 콜백 함수가 없을 때까지 지속된다.
Node.js의 블로킹 I/O
Event Callback을 사용하는 Node.js
의 이벤트 모델은 아래의 4개의 블로킹 I/O를 가진다.
- 파일 읽기
- 데이터베이스 질의
- 소켓 요청
- 원격 서비스 접속
Node.js
는 블로킹 I/O로 인한 지연을 피하고자 Event Callback을 사용하는데, 블로킹 I/O를 발생시킨 요청이 있으면 해당 요청에 대한 처리를 백그라운드의 다른 Thread에서 처리한다. 이벤트 큐에서 블록 I/O이벤트가 발생하면 Node.js
는 메인 이벤트 루프 Thread가 아닌, 백그라운드에서 돌고있는 Thread 풀에서 Thread 한 개를 꺼내고 해당 이벤트를 처리하는 것이다.
Node.js의 이벤트 모델 전체
Request가 들어오면 단일 스레드인 Node.js Application
에서 요청을 받아 Event Queue
에 넣어준다. Event Loop
는 Event Queue
에 들어와있는 작업들을 순서대로 Thread pool에서 Thread를 하나 꺼내 작업을 할당해주고 콜백 함수를 다시 Event Queue
에 넣어준다.