Node.js의 이벤트 루프

이벤트 루프는 자바스크립트가 단일 쓰레드 기반임에도 불구하고 Node.js가 non-blocking I/O 작업을 수행할 수 있도록 해주는 핵심 기능이다.

이벤트 루프의 각 단계

이벤트 루프에는 6개의 단계(Phase)가 있다. 각 단계는 단계마다 처리해야하는 콜백 함수를 담는 큐를 가지고 있다.

자바스크립트 코드는 idle, prepare 단계를 제외한 어느 단계에서나 실행될 수 있다.

nextTickQueuemicroTaskQueue는 이벤트루프의 구성요소는 아니고 이 큐의 작업도 이벤트 루프의 상태와 상관없이 실행될 수 있다.

Timer 단계

이벤트 루프가 시작하는 단계다. setTimeout이나 setInterval과 같은 함수를 통해 만들어진 타이머들을 큐에 넣고 실행한다.

큐에 들어가는 타이머들은 실행할 시간이 같거나 지난 타이머들이다. 실행시간을 측정하는 기준은 now - registeredTime >= delta 와 같이 계산한다. 여기서 delta는 setTimeout 등에 매개변수로 사용되는 ms이다.

setTimeout(
  callbackFunction,
  1000 // this is delta
)

큐에 들어가기 이전의 타이머들은 최소 힙으로 관리된다. 최소 힙은 가장 낮은 수의 노드를 루트로 가지는 자료구조이다. 타이머들이 들어있는 최소 힙에서는 실행시간이 기준이 되고 실행시간이 가장 적게 남은 타이머가 항상 루트가 된다. 그렇기 때문에 타이머를 매번 정렬할 필요 없이 항상 가장 먼저 실행될 타이머를 알 수 있다.

Timer 단계에서는 최소 힙의 타이머들을 순차적으로 찾아 실행하고 힙을 재구성한다. 이 때 순차적으로 타이머를 실행하기 때문에 특정 타이머가 아직 실행하지 않을 단계라면 그 이후의 타이머들은 검사를 생략한다.

Pending (i/o) 콜백 단계

이전 작업들의 콜백이 pending_queue에서 대기 중인지 검사한다. 만약 대기 중인 콜백이 있다면 시스템 실행 한도에 도달할 때까지 꺼내어 실행한다.

여기서 큐에 들어있는 콜백들은 현재 돌고 있는 루프 이전의 작업에서 큐에 들어온 콜백이다.

Idle, Prepare 단계

노드의 내부 동작을 위한 단계이다.

Poll 단계

이벤트 루프 중 가장 중요한 단계이다. 새로운 io 이벤트를 가져와서 관련 콜백을 수행한다. 예를 들면 소켓 연결과 같이 새로운 커넥션이나, 파일 읽기 같은 데이터 처리 수행한다.

이 단계에서는 watch_queue를 가지고 있다. 해당 큐가 비어있지 않다면 동기적으로 모든 콜백을 실행한다. 실행은 큐가 비거나 시스템 실행 한도에 다다를 때까지이다.

watchqueue의 작업을 모두 처리하면(= 큐가 비면) checkqueue, pendingqueue, closingcallbacks_queue에 남은 작업이 있는지 검사하고 다음 작업이 있다명 다음 단계로 이동한다.

만약 다른 큐에도 작업이 없다면 다음 타이머의 실행 시점까지 대기한다. 모든 큐가 비었다면 무의미하게 이벤트 루프를 돌게 되기 때문에 대기하는 것이다.

Check 단계

setImmediate의 콜백만을 위한 단계이다.

check_queue가 비거나 실행한도에 도달할 때까지 콜백을 수행한다.

Close 콜백 단계

close나 destroy 등의 이벤트, 타입의 콜백이 처리되는 단계이다. 이벤트 루프는 Close 콜백 단계를 마치면 이벤트 루프를 한 번 더 돌지, 종료할지를 검사한다.

만약 처리해야할 작업이 남아있지 않다면 이벤트 루프를 종료한다.


참조


Written by@박대성

독서와 지식관리에 관심이 많은 개발자

GitHub