본문 바로가기

Node.js

Node.js 동작원리

node.js 란?

Javascript를 브라우저 밖에서도 실행 할 수 있도록 하는 Javascript의 런타임
비동기(Asynchronous), 이벤트 주도(Event-driven), Non-Blocking I/O, 확장성

런타임 ? 
특정 언어로 만든 프로그램을 실행할 수 있는 환경

프로세스
메모리에 올라와 실행되고 있는 프로그램의 인스턴스.
실행되고 있는 프로그램(독립적인 개체)

스레드
프로세스 내에서 할당받은 실행의 단위.
스레드는 프로세스 당 CPU의 코어 개수만큼 생성될 수 있다.
* 스레드는 프로세스 내의 메모리 공간을 공유하지만, 각각의 프로세스는 별도의 메모리 공간을 갖는다.

싱글스레드 논블로킹 모델

하나의 스레드로 동작하지만, 비동기 I/O 작업을 통해 요청들을 서로 블로킹하지 않는다.
즉, 동시에 많은 요청들을 비동기로 수행함으로써 싱글스레드일지라도 논블리킹이 가능하다.

클러스터링을 통해 프로세스를 포크(fork)하여 멀티스레드인것 처럼 사용될 수 있다.
트래픽에 따라서 프로세스를 포크할 수 있으므로 확장성이 용이하다.

Node.js 는 완전한 싱글스레드 ??

Javascript를 실행하는 스레드는 메인스레드 단 하나이다.
하지만 Node.js가 완전하게 싱글스레드를 기반으로 동작하지는 않는다.
일부 Blocking 작업들은 libuv의 스레드 풀에서 수행되기 때문이다.

이벤트 기반(Event-driven)

이벤트가 발생할 때 미리 지정해둔 작업을 수행하는 방식.
이벤트 리스너에 등록해둔 콜백함수를 실행하는 방식으로 동작한다.

이벤트 루프

이벤트에 따라 호출되는 콜백함수 관리

Node.js의 구성

내장 라이브러리, v8 엔진, libuv 로 구성되어있다.
이벤트 기반, 논블로킹 I/O 모델들은 모두 libuv 라이브러리에서 구현된다.

Node.js에서 작성되는 거의 모든 코드들은 콜백함수로 이루어져 있다.
콜백 함수들은 libuv 내에 위치한 이벤트 루프에서 관리 및 처리된다.
이벤트 루프는 여러 개의 페이즈(Phase)들을 갖고 있으며, 해당 페이즈들은 각자만의 큐(Queue)를 갖는다.

이벤트 루프는 라운드 로분(round-robin)방식으로 노드 프로세스가 종료될때까지 일정 규칙에 따라
여러개의 페이즈들을 계속 순회한다.
페이즈들은 각각의 큐들을 관리하고, 해당 큐들은 FIFO 순서로 콜백 함수들을 처리한다.

논블로킹 I/O

Node.js에서의 논블로킹 I/O 모델은 Input과 Output이 관련된 작업(http, Database CRUD, third party api, filesystem 등)의 블로킹 작업들을 백그라운드에서 수행하고, 이를 비동기 콜백함수로 이벤트 루프에 전달하는 것을 말한다.

I/O들은 OS 커널 혹은 libuv 내의 스레드 풀에서 담당한다.
libuv는 OS 커널에서 어떤 비동기 작업들을 지원해주는지 알고있기 때문에 작업 동류에 따라 커널 혹은 스레드풀로 분기한다.
작업이 완료되면 이벤트루프에게 이를 알려주고, 이벤트 루프에 콜백함수로 등록된다.
libuv의 스레드 풀은 커널이 지원안하는 작업을 수행한다.
Node.js가 완전한 싱글스레드는 아닌 이유는 libuv의 스레드풀은 멀티스레드로 이루어져 있기 때문이다.

이벤트 루프 동작 원리

  1. timers
    serTimeout() 과 setInterval() 같은 timer 함수들이 처리된다.
    이벤트 루프가 페이즈를 순회하면서 timer 단계에 오면 처리할 수 있는 timer 함수들을 확인하고 콜백 함수를 실핸한다.
    timer 함수의 입련된 지연시간은 콜백함수가 실행되는 정확한 값이 아니라 단지 해당 지연시간 이후에 실행된다는 기준시간이다.

  2. pending callbacks

다음 루프 반복으로 연기된 I/O 완료 결과가 큐에 담긴다.
I/O 작업이 완료되면 다음번 루프에 이 단계로 들어오고 I/O 작업 블록내의 콜백 함수들을 poll 단계의 큐로 넘겨준다.
또한 TCP 오류 같은 시스템 작업의 콜백을 실행한다.

  1. idle, prepare
    내부용으로만 사용되기 때문에 스킵

  2. poll
    I/O와 연관된 콜백(클로즈 콜백, 타이머로 스케줄링된 콜백, setImmediate()를 제외한 모든 콜백)을 실행한다.
    또한 timer 단계에서의 실행 시간 제어를 담당한다.
    이 단계에서는 poll 큐에 쌓인 콜백함수들을 한도가 넘지 않을때까지 모두 동기적으로 실행한다.
    만약 한도가 넘거나, 더이상 실행할 콜백함수가 없을때는 별도의 규칙에 따라, 다음 단계로 넘어가거나 대기한다.

  3. check
    setImmediate()의 콜백함수가 실행된다.
    이벤트 루프가 poll 단계에서 작업을 수행한 뒤, poll 단계가 유휴상태가 되었다면
    poll 이벤트를 기다리지 않고 check 단계로 넘어가게 된다.

  4. close callbacks
    close 이벤트에 따른 콜백함수를 실핸한다.
    예를 들면 socket.on('close', ...)

마무리

간단하게 노드가 동작하는 원리에 대해서 정리해보았다.
아직 부족하지만 기본 동작이나 장점들을 살려 개발할 수 있도록 공부를 더 할 예정이다.

'Node.js' 카테고리의 다른 글

Lodash  (0) 2020.10.10
Moment.js  (0) 2020.10.09