Node.js의 기초(내부 아키텍처, 이벤트루프와 워커, 비동기의 이해)

2021. 3. 18. 03:14 JavaScript BackEnd/Node.js, Express

1. Nodejs 란?

2009년에 Ryan Dahl이 발표했다. 노드는 구글의 V8 JavaScript 엔진에 기반한다. 싱글스레드 기반 비동기 이벤트 위주 IO를 사용하는 고도의 확장성을 가진 시스템이다. 간단한 작업을 수행하지만 빈도가 높은 웹 어플리케이션에 이상적이다.

특징

  • 싱글 쓰레드 : 문맥교환으로 인한 오버헤드X
  • 비동기 IO : CPU time loss를 피함, io요청이 있으면 워커한테 던져놓고 쓰레드는 다른일을 계속 받는다.
  • 이벤트 기반 : epool 또는 kqueue를 사용
  • 경량 프레임워크
  • 풍부한 라이브러리 : 각종 모듈
  • 서버와 클라이언트에서 사용하는 언어(JS)가 같음

2. Nodejs 아키텍처

구글 이미지 검색에서 나오는 노드아키텍처들 중에 libev가 있는 건 옛날 사진이다. 노드0.9버전부터는 libev의 종속성을 제거하였다.

 

상위레벨은 자바스크립트로 되어있고, 로우레벨은 C,C++로 짜여있다. 크게 보면

  • 자바스크립트 라이브러리
  • 바인딩 : js를 C, C++로 바인딩처리
  • V8엔진
  • libuv : 리눅스의 libeio와 윈도우의 IOCP를 추상화시켜서 맨 위에서 동작

2-1. 진짜 싱글쓰레드만 쓰나?

싱글스레드 기반의 이벤트루프가 계속 돌면서 요청을 처리한다. io요청이 있는 경우, 쓰레드풀에 던져둔다. 여기에는 워커들이 여러개있다. 사용자가 작성할때가 싱글쓰레드다.

즉, 이벤트루프만 싱글스레드라서 req, res이 싱글쓰레드인거지 뒤에서 열심히 일하는 워커(io처리)들은 여러개(멀티쓰레드)이다.

이벤트루프만 blocking안되게 만들면, 워커들은 알아서 일나눠가지고 돌아간다.

2-2. 비동기가 왜 좋은가?

동기식 IO에서는 디스크에 파일쓰기요청이 들어오면, 디스크가 파일쓰는 동안에 cpu가 논다. 다쓰면 return이 돌아와서 그다음 코드가 진행된다.

비동기에서는 저런 요청이 들어오면 워커한테 일준다. 그러고선 계속 cpu가 쉬지않고 다른 요청들을 받는다. io처리가 끝나면 워커가 콜백함수와 함께 파일쓰기가 끝났다고 알려준다. cpu 효율입장에서 엄청난 차이인 거다.

2-3. 싱글쓰레드로 여러 요청을 어떻게 받는가?

멀티플렉싱으로 해결한다.

2-4. V8 JavaScript 엔진

구글에서 개발된 오픈 소스 JIT 가상 머신형식의 자바스크립트 엔진이며 구글 크롬 브라우저와 안드로이드 브라우저에 탑재된다.

V8은 자바스크립트를 바이트코드로 컴파일하거나 인터프리트하는 대신 실행하기 전 직접적인 기계어로 컴파일하여 성능을 향상시킨다. 추가적인 속도향상을 위해 인라인 캐싱과 같은 최적화 기법을 적용한다.

3. 기존 웹서버와 뭐가 다른가?

Apache와 같은 웹서버는 웹 리소스가 요청될 때마다 요청을 처리하기 위해 매번 별도의 스레드를 생성하거나, 새 프로세스를 호출한다.

request가 많은 경우, 병목구간이 흔히 발생하는데, 까보면 로직보다는 IO에서 문제(서버에서 IO처리하다가 지연발생으로 다른 요청들이 처리되지 않음)가 많다. 동기 방식에서 이를 해결하기 위해 흔히 멀티쓰레드에 스케일아웃하고 로드밸런싱을 한다.

하지만 Node와 같이 비동기로 처리하게 될 경우, 훨씬 가벼우면서도 빠른데, 요청 처리가 완료되기전에 제어권을 다음 요청으로 넘긴다. 논블로킹으로 요청들을 처리할 수 있는 것이다.

Node.js는 싱글스레드 위에서 비동기로 돌아간다. URL_A가 들어오면 이벤트를 돌리고, 또 다음 URL_B를 받고 이벤트 돌리고, 그 와중에 A가 완료되면 콜백해주는 방식이다.

단일 쓰레드이기 때문에 오버헤드가 적어 어플리케이션 확장성을 쉽게 얻을 수 있다. 또한 리소스 사용량을 최소화하였기 때문에 멀티스레드로 굳이 만들 필요가 없다.

4. 비동기를 코드로 이해하기

파일읽기를 하는 IO처리 상황이다. 굿

동기방식

var fs = require('fs');

var content = fs.readFileSync("readme.txt", "utf-8");

console.log(content);
console.log('파일 읽기...');

//결과
readme.txt의 파일내용이 다 찍힌 뒤
파일 읽기...

비동기방식

var fs = require('fs');

fs.readFile("readme.txt", "utf-8", function(err, content) {
    console.log(content);
});

console.log('파일 읽기...');

//결과
파일 읽기... 후에
readme.txt의 파일내용이 다 찍힘

5. node 명령어

5-1. 도움말

node --help

5-2. 기본 명령어 : node [소스.js] [옵션]

  • node : REPL로 들어감
  • node -v : 버전
  • node -e : 스크립트 평가
  • node -p : 스크립트 평가
  • node -c : 문법 체크
  • node -r : 모듈을 미리 로딩



출처: https://sjh836.tistory.com/79?category=710138 [빨간색코딩]