Node.js 를 공부하다 이벤트 루프라는 개념이 어려워 정리해두었다.
이벤트 루프란?
Node.js 는 하나의 스레드(싱글 스레드)에서 작업을 처리.
이벤트 루프란 시스템 커널에서 가능한 작업이 있다면 그 작업을 커널에 이관하여 JavaScript 가 싱글 스레드임에도 논블로킹(비동기) I/O 작업을 수행할 수 있게 해주는 기능.
이벤트 루프의 단계
- 아래 그림의 화살표 방향으로 루프가 실행. (timers → pending callbacks …. → close callbacks → timers …)
- 각 단계에는 각각 실행할 콜백 함수를 담기 위한 큐가 존재
- 이벤트 루프가 특정 단계에 진입하면 해당 단계에서 필요한 작업을 수행 후, 해당 단계의 큐에서 콜백을 실행. 실행하여 큐가 모두 비거나 콜백 제한 수에 도달하게 된다면 이벤트 루프가 다음 단계로 이동.
- js 코드는 유휴, 준비(idle, prepare) 단계를 제외한 어느 단계에서나 실행될 수 있음
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
* 틱(tick) ? 이벤트 루프에서 다음 단계로의 이동을 말함.
타이머 단계(timers)
- setTimeout(), setInterval() 에 의해 만들어진 타이머를 큐에 넣고 실행.
- 실행할 시간이 지난 타이머들이 대상. 대상인 콜백이 무한정 실행되는 것은 아니고 시스템의 실행 한도에 도달하면 다음 단계로 넘어감.
- 타이머들은 최소 힙으로 관리되며, 힙을 구성할 때 기준으로 실행할 시각이 가장 적게 남은(가장 빠르게 실행할 수 있는) 타이머가 힙의 루트가 됨
대기 콜백 단계(pending callbacks)
- 이 단계의 큐에는 현재 돌고 있는 이벤트 루프 이전의 작업에서 큐에 들어온 콜백이 담김.
- TCP 에러 등 일부 시스템 작업에 대한 콜백을 실행.
- 예) TCP 소켓이 연결을 시도할 때 ECONNREFUSED(연결이 되지 않았을 때) 오류를 수신할 경우, 이 오류를 보고하기 위해 일부 *nix 시스템(Unix, Linux 등 유사 운영 체제)은 대기할 수 있음.
- 대기 단계에 들어왔을 때 이전 작업들의 콜백이 큐에서 대기 중인지 검사 후 실행 대기 중이라면 시스템 실행 한도에 도달할 때까지 꺼내어 실행.
유후, 준비 단계(idle, prepare)
- Node.js 의 내부 동작을 위해서만 사용됨
- 유휴 단계는 틱마다 실행되며, 준비 단계는 매 폴링(poll) 직전에 실행.
폴 단계(poll)
- 새로운 I/O 이벤트를 찾고, I/O 관련 콜백을 실행.
- 이 단계에서는 watch_queue 라는 큐를 갖고 있다.
- 이벤트 루프가 폴 단계에 진입한 상태에서 예약된 타이머가 없을 경우 다음 중 하나 발생
- 폴 큐가 비어있지 않을 경우 큐가 모두 소진 되거나 시스템 제한에 도달할 때까지 동기적으로 폴 큐의 모든 콜백을 실행.
- 폴 큐가 비어있을 경우
- 체크 단계의 큐, 대기 단계의 큐, 종료 단계의 큐에 남은 작업이 있는지 검사
- setImmediate() 콜백이 있는 경우, 폴 단계를 종료하고 체크 단계로 이동
- setImmediate() 없을 경우 콜백이 큐에 추가될 때까지 대기 후 바로 실행
- 폴 큐가 비어있으면 이벤트 루프는 타이머 단계에서 처리할 타이머가 있는지 확인하고 존재할 경우 타이머 단계로 돌아감
체크 단계(check)
- setImmediate() 의 콜백만을 위한 단계로, 큐가 비거나 시스템 실행 한도에 도달할 때까지 콜백 수행
- 폴 단계에서 큐가 비어있고 setImmediate() 로 스케줄된 콜백이 있을 경우, 폴 이벤트를 기다리지 않고 바로 체크단계 진행
종료 콜백 단계(close callbacks)
- 소켓이나 핸들러가 갑자기 닫힐 경우, 혹은 process.nextTick() 을 통해 해당 이벤트 발생
- 이벤트 루프는 종료 콜백 단계를 마치고 난 후 다음 루프에서 처리할 작업이 남아있는 지 검사. 작업이 남아있을 경우 타이머 단계부터 다시 루프를 돌고, 아닐 경우 루프 종료.
<참고>
NestJS 로 배우는 백엔드 프로그래밍 - 한용재 저
node.JS 공식 문서
댓글 피드백은 언제나 환영합니다!
'BackEnd' 카테고리의 다른 글
TypeORM Repository 조건(옵션) 안될 때 (2) | 2024.09.10 |
---|---|
Nest-cli로 프로젝트 생성 시 git 추적 안되는 문제 (0) | 2024.08.21 |
JPA - 영속성 컨텍스트 (1) | 2024.01.15 |
DDD - 엔티티와 밸류 (0) | 2024.01.12 |
JPA 빠르게 훑어보기 (0) | 2023.08.20 |