Nodejs
Pending: 보류중 Callbacks Phase
- 이 페이즈에는 Pending_queue에 담기는 콜백들을 관리한다.
- 이 큐에 담기는 콜백들은 이 전 이벤트 루프 반복에서 수행되지 못했던 I/O 콜백들이다.
- Time Phase에서 말했듯 대부분의 페이즈는 시스템의 실행 한도의 영향을 받는다.
- 시스템의 실행 한도 제한에 의해 큐에 쌓은 모든 작업을 실행하지 못하고 다음 페이즈로 넘어갈 수도 있다.
- 이때 처리하지 못하고 넘어간 작업들을 쌓아놓고 실행하는 페이즈다.
- 에러 헨들러 콜백 또한 pending_queue로 들어오게 된다. *nix는 TCP 단에서 ECONNREFUSED( : 연결 서버에 의해 거부)를 받으면 pending_queue에 에러 핸들러를 추가한다.
static int uv__run_pending(uv_loop_t* loop) {
QUEUE* q;
QUEUE pq;
uv__io_t* w;
if (QUEUE_EMPTY(&loop->pending_queue)) // pending_queue가 비어있다면 바로 0을 리턴한다
return 0;
QUEUE_MOVE(&loop->pending_queue, &pq);
while (!QUEUE_EMPTY(&pq)) {
q = QUEUE_HEAD(&pq);
QUEUE_REMOVE(q);
QUEUE_INIT(q);
w = QUEUE_DATA(q, uv__io_t, pending_queue);
w->cb(loop, w, POLLOUT); // 큐에 담겨있던 콜백을 실행한다
}
return 1;
}
실제 Pending Callback Phase에서 콜백을 실행하는 코드이다
만약 pending_queue에 담겨있는 콜백이 없다면 그 즉시 0을 리턴하고 다음 페이즈로 넘어간다.
만약 pending_queue에 콜백이 담겨있다면 시스템의 실행 한도를 넘어가기 전까지 큐에 있는 모든
콜백을 순서대로 실행한다. 그리고 pending 되었던 작업을 실행했다는 의미로 1을 반환한다.
uv_run_pending의 반환값은 event loop의 mode가 UV_RUN_ONCE일 때 Poll Phase가 기다리는 시간을 결정하는데 영향을 끼친다. 하지만 Node.js의 event loop의 mode는 기본적으로 UV_RUN_DEFAULT이므로 신경쓰지 않아도 된다.
event loop의 mode는 UV_RUN_DEFAULT, UV_RUN_ONCE, UV_RUN_NOWAIT가 존재하며 각 mode에 대한 자세한 설명은 공식 문서에서 확인할 수 있다.
Idle: 게으른, 쉬고 있는 , Prepare Phase
- 이 페이즈들은 Node.js의 내부적인 관리를 위해 JavaScript를 실행하지 않는다.
- 공식 문서에서도 별다른 설명이 없고 코드의 직접적인 실행에 영향을 미치지 않는다.
Poll: 투표, 여론조사 Phase
- 이 페이즈는 새로운 I/O 이벤트를 다루면 watcher_queue의 콜백들을 실행한다. // watcher : 당직자
- watcher_queue에는 I/O에 대한 거의 모든 콜백들이 담긴다.
- 쉽게 말해 setTimeout, setImmediate, close 콜백 등을 제외한 모든 콜백이 watcher_queue에서 실행된다고 생각하면 된다.
// watcher_queue 에 담긴 비동기 콜백들의 종류
데이터 베이스에 쿼리를 보낸 후 결과가 왔을 때 실행되는 콜백
HTTP 요청을 보낸 후 응답이 왔을 때 실행되는 콜백
파일을 비동기로 읽고 다 읽었을 때 실행되는 콜백
Poll Phase가 콜백을 관리하는 방법
- 우선, Poll Phase가 어떻게 새로운 I/O 이벤트에 대한 콜백을 다루는지부터 알아보자
- Poll Phase가 watcher_queue에 담긴 콜백들을 관리 한다고 하였다.
- 하지만 I/O 이벤트는 타이머와 달리 큐에 담긴 순서대로 I/O 작업이 완료되어 콜백 또한 차례대로 실행된다는 보장이
없다.
- 데이터 베이스에 A, B 쿼리를 순서대로 날려도 응답은 B, A의 순서로 올 수도 있다.
- A를 B 보다 먼저 실행하기 위해 A 응답이 올 때까지 B의 콜백 처리를 미루는 것은 말도 안되는 일이다.
- 큐에 담긴 순서와 무관하게 B를 먼저 실행하는 것은 당연하다.
- 실행을 가지고 있던 타이머와 달리 I/O 이벤트는 이벤트 루프 혼자서는 언제 완료 되어있는지 알 수가 없다.
- 이런 문제를 해결하기 위해 Poll Phase는 단순한 콜백 큐를 사용하지 않는다.
- 이벤트 루프가 n 개의 열린 소켓을 가지고 있고 n 개의 완료되지 않은 요청이 있다고 가정할 때
- 이 n 개의 소켓에 대해 소켓과 메타 데이터를 가진 watcher를 관리하는 큐가 watcher_queue이다.
- 그리고 각 watcher는 FD( File Descriptor: 기술자)를 가지고 있다. 이 FD는 네트워크 소켓, 파일 등을 가리킨다.
- 운영체제가 FD가 준비 되었다고 알리면 이벤트 루프는 이에 해당하는 watcher를 찾을 수 있고 watcher가 맡고 있던
콜백을 실행할 수 있다.
※ Poll Phase 또한 시스템 시행 한도의 영향을 받는다.
Poll Phase Blocking: 막다 방해하다.
- Node.js가 Poll Phase에 진입했을 때 기다리고 있는 I/O 요청이 없거나, 아직 응답이 오지 않았을 때
- Timer Phase Pending Callback Phase와 달리 바로 다음 페이즈로 넘어가지 않고 조금 다르게 작동한다.
- Node.js가 다음 페이즈로 이동해 Poll Phase로 올 때까지 실행할 수 있는 작업이 있는지 고려한다.
- Poll Phase에 진입해 콜백들을 실행해 watcher_queue가 비게 된다면, 또는 처음부터 watcher_queue가 비어있었다면
이벤트 루프는 Poll Phase에서 잠시 대기(timeout :
농구·배구 등의 경기에서, 경기를 일시 중지시켜 선수의 교체·휴식·작전의 협의 등을 행하도록 규정된 짧은 시간. 경기 시간에 포함되지 않음. 타임.
) 할 수 있다.
이벤트 루프가 종료되었다면 바로 다음 페이즈로 넘어간다.
만약 Close Callbacks Phase, Pending Callbacks Phase에서 실행할 작업이 있다면 바로 다음 페이즈로 넘어간다.
만약 Timer Phase에서 즉시 실행할 수 있는 타이머가 있다면 바로 다음 페이즈로 넘어간다.
만약 Timer Phase에서 즉시 실행할 수 있는 타이머는 없지만 n초 후에 실행할 수 있는 타이머가 있다면
n초 기다린 후 다음 페이즈로 넘어간다.
Check Phase
- 이 페이즈는 오직 setImmediate의 콜백만을 위한 페이즈이다.
- setImmediate가 호출되면 Check Pase의 큐에 담기고 Node.js가 Check Phase에 진입하면 차례대로 실행한다.
- 공식 문서에서 setImeediate와 process.nextTick의 차이에 주목한다.
process.nextTick은 같은 페이즈에서 호출한 즉시 실행된다.
setImmediate는 다음 틱에서 실행된다. 정확히는 Node.js가
틱을 거쳐 Check Phase에 진입하면 실행된다.
- process.nextTick은 즉시 실행되고 setImeediate는 다음 틱에 실행된다.
- 공식 문서에서도 두 이름은 바뀌어야 된다고 하지만 이미 많은 모듈들이 위 두가지의 뒤바뀐 동작에 의존해 동작하고 있어 이름을 바꾸지 못했다고 한다.
Close Callbacks Phase
- socket.on('close', () => {})과 같은 close 이벤트 타입의 핸들러를 처리하는 페이즈이다.
- 정확하게는 uv_close()를 부르면서 종료된 핸들러의 콜백들을 처리하는 페이즈이다.
static void uv__run_closing_handles(uv_loop_t* loop) {
uv_handle_t* p;
uv_handle_t* q;
p = loop->closing_handles;
loop->closing_handles = NULL;
while (p) { // close queue에 담긴 콜백을 실행한다
q = p->next_closing;
uv__finish_close(p);
p = q;
}
}
실제 Close Callbacks Phase 코드로써 시스템 실행 한도를 초과하기 전까지 closeing_handles에 담긴 순서대로 실행한다.
Node.js 이벤트 루프(Event Loop) 샅샅이 분석하기
글에 들어가기에 앞서 Node.js의 이벤트 루프의 경우 공식 문서에 설명이 부족하고 이에 따라 여러 사람들이 각자 나름대로 분석한 글이 많아 무엇이 이벤트 루프의 정확한 동작인지 알기 힘듭니
www.korecmblog.com