V-logue

Node.js와 Single Thread 본문

발자취/Node.js

Node.js와 Single Thread

보그 2024. 2. 8. 16:59

node.js

 

정말 오랜만에, 블로그에 글을 쓰는데 기술면접도 준비할 겸 Node.js에 대해서 다시 알아보기로 결정했습니다. 

Javascript로 서버개발을 하고 있는 모든 서버 개발자의 면접 질문 중 단골 손님인

 

모두가 안다고 말하지만, 모두가 모르는 Node.js 그에 대해서 알아보자.

 


what is node.js?

 

node.js의 공식 홈페이지인 nodejs.org를 보면, node.js에 대해서 이렇게 말하고 있습니다.

 

node.js는 오픈 소스 및 크로스 플랫폼의 Javascript 런타임 환경입니다.

node.js는 브라우저 외부에서 Google Chrome의 V8 Javascript 엔진을 통해 실행되고, node.js의 성능이 향상됩니다.

 

node.js앱은 모든 요청에 대해 스레드를 새롭게 생성하지 않고, 단일 프로세스에서 실행됩니다.

node.js는 Javascript 코드가 blcoking되는 것을 방지하는 일련의 비동기 I/O 기본 요소를 표준 라이브러리에 제공하며 일반적으로 Non-blocking 패러다임을 사용하여 작성되기 때문에 blocking 자체가 표준이 아닌 예외로 처리됩니다.

 

node.js가  I/O operations를 수행하고 network를 읽어 들이고 데이터베이스 작업과 같은 I/O 처리를 할 때

스레드를 차단하고, 대기중인 CPU를 낭비하는 대신 Node.js는 응답이 돌아올 때 재개합니다.

 

이를 통해 Node.js는 심각한 버그 소스가 될 수 있는 스레드 동시성 관리 부담 없이 단일 서버로 수천 개의 동시 연결을 처리할 수 있습니다.

 

Node.js — Introduction to Node.js

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org


공식 사이트의 node.js의 설명을 간단하게 요약하면,

Node.js는 빠른 성능과 동시 연결 처리를 위해 만들어진 JavaScript를 실행하는 runtime환경입니다.

공식 사이트의 설명을 차례차례 해석하면 먼저

V8

V8은 C++로 작성된 Google의 오픈소스 고성능 JavaScript 및 WebAssembly 엔진으로, Chrome과 Node.js 등에서 사용된다 - 라고 공식문서에 나와 있습니다.

그렇다면 V8은 어떻게 JS 코드를 빠르게 실행시키는 것 일까요? 

 

V8의 동작 방식은 다음과 같습니다.

  1. V8은 소스 코드(JS)를 먼저 인터프리터를 통해 바이트코드 형태로 해석합니다
  2. V8은 인터프리터에 의해 생성된 바이트코드를 추적하면서, 프로파일링 데이터를 만들게 되는데 여기서 자주 실행되는 부분을 핫스팟으로 인식합니다.
  3. 핫스팟으로 인식된 코드는 더 효율적인 기계어로 컴파일 됩니다.
  4. 그리고 기계어로 인식된 코드는 캐싱되어 사용되고, 다시 같은 코드가 여러번 불리면 해당 코드를 핫스팟으로 인식하고 다시 컴파일하게 됩니다.
  5. 그리고 컴파일된 코드들은 OS가 바로 실행할 수 있는 상태로 Client의 요청이 있을 때 바로 실행됩니다.

위와 같은 방식을  JIT(Just-In-Time) 컴파일이라고 부르고, V8은 빠르게 JS 코드를 실행할 수 있습니다.

 

Single Tread(싱글 스레드)

node.js에서 항상 빠지지 않는 것이 싱글 스레드라는 개념이다.

 

일반적으로 확실한 것은 Javascript는 싱글 스레드

 

node.js

 

정말 오랜만에, 블로그에 글을 쓰는데 기술면접도 준비할 겸 Node.js에 대해서 다시 알아보기로 결정했습니다. 

Javascript로 서버개발을 하고 있는 모든 서버 개발자의 면접 질문 중 단골 손님인

 

모두가 안다고 말하지만, 모두가 모르는 Node.js 그에 대해서 알아보겠습니다.

 


what is node.js?

 

node.js의 공식 홈페이지인 nodejs.org를 보면, node.js에 대해서 이렇게 말하고 있습니다.

 

node.js는 오픈 소스 및 크로스 플랫폼의 Javascript 런타임 환경입니다.

node.js는 브라우저 외부에서 Google Chrome의 V8 Javascript 엔진을 통해 실행되고, node.js의 성능이 향상됩니다.

 

node.js앱은 모든 요청에 대해 스레드를 새롭게 생성하지 않고, 단일 프로세스에서 실행됩니다.

node.js는 Javascript 코드가 blcoking되는 것을 방지하는 일련의 비동기 I/O 기본 요소를 표준 라이브러리에 제공하며 일반적으로 Non-blocking 패러다임을 사용하여 작성되기 때문에 blocking 자체가 표준이 아닌 예외로 처리됩니다.

 

node.js가  I/O operations를 수행하고 network를 읽어 들이고 데이터베이스 작업과 같은 I/O 처리를 할 때

스레드를 차단하고, 대기중인 CPU를 낭비하는 대신 Node.js는 응답이 돌아올 때 재개합니다.

 

이를 통해 Node.js는 심각한 버그 소스가 될 수 있는 스레드 동시성 관리 부담 없이 단일 서버로 수천 개의 동시 연결을 처리할 수 있습니다.

 

Node.js — Introduction to Node.js

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org


공식 사이트의 node.js의 설명을 간단하게 요약하면,

Node.js는 빠른 성능과 동시 연결 처리를 위해 만들어진 JavaScript를 실행하는 runtime환경 입니다.

공식 사이트의 설명을 차례차례 해석하면 먼저

V8

V8은 C++로 작성된 Google의 오픈소스 고성능 JavaScript 및 WebAssembly 엔진으로, Chrome과 Node.js 등에서 사용된다 - 라고 공식문서에 나와 있습니다.

그렇다면 V8은 어떻게 JS 코드를 빠르게 실행시키는 것 일까요? 

 

V8의 동작 방식은 다음과 같습니다.

 

1. V8은 소스 코드(JS)를 먼저 인터프리터를 통해 바이트코드 형태로 해석합니다.

2. V8은 인터프리터에 의해 생성된 바이트코드를 추적하면서, 프로파일링 데이터를 만들게 되는데 여기서 자주 실행되는 부분을 핫스팟으로 인식합니다.

3. 핫스팟으로 인식된 코드는 더 효율적인 기계어로 컴파일 됩니다.

4. 그리고 기계어로 인식된 코드는 캐싱되어 사용되고, 다시 같은 코드가 여러번 불리면 해당 코드를 핫스팟으로 인식하고 다시 컴파일하게 됩니다.

5. 그리고 컴파일된 코드들은 OS가 바로 실행할 수 있는 상태로 Client의 요청이 있을 때 바로 실행됩니다.

 

위와 같은 방식을  JIT(Just-In-Time) 컴파일이라고 부르고, V8은 빠르게 JS 코드를 실행할 수 있습니다.

 

Single Tread(싱글 스레드)와 Non-blocking I/O

node.js에서 항상 빠지지 않는 것이 싱글 스레드라는 개념입니다.

 

일반적으로 확실한 것은 Javascript는 싱글 스레드라는 것 입니다.

따라서, 전역 컨텍스트라면 다르겠지만 일반적으로 함수 단위로 하나의 컨텍스트를 갖고 가장 먼저 실행된 첫 번째 콜스택이 종료되야 다음 스택이 실행되는 구조입니다.

이런 구조라면, 동시성 문제는 해결되겠지만 동기적 작업들의 I/O가 blocking되고, 확장성이 떨어지며 긴 작업 하나를 기다리기 위해 나머지가 대기하는 구조가 됩니다.

 

node.js는 이런 문제를 해결하기 위해 다음과 같은 작업 방식을 사용합니다.

 

물론 개념적으로 node.js 또한 싱글 스레드라고 표현합니다.

흔히들 하는 오해가 메인 스레드와 이 스레드를 보좌하는 이벤트 루프를 위한 스레드가 총 2개가 있는데 작동 방식이,

싱글 스레드인 것 처럼 돌아간다고 생각하시는 분들이 있습니다.

이벤트 루프 또한 메인 스레드안에서 실행되며, cb작업이 실행될 수 있도록 지원합니다.

 

1. Client나 특정 요청자로부터 요청이 서버로 들어오면 메인 스레드를 타고 이벤트 루프로 도착합니다.

2. 이벤트 루프는 들어온 요청이 다른 요청들을 막을 수 있는 Blocking I/O 인지 판별합니다.

3. Non-Blocking I/O로서 작업할 수 있다면 커널을 거쳐 Event Queue에 cb를 등록하고 등록된 cb는 이벤트 루프가 call stack이 비어 있다면 call stack으로 적절히 이동시켜 메인 스레드에의해 실행되도록 합니다.

4. 만약 Blocking I/O라면 Node.js 내의 libuv가 libuv 내의 별도의 Thread Pool에서 Worker Thread를 선택하여 작업을 위임합니다. Worker Thread는 작업을 완료한 후 Event Queue로 cb 등록합니다.

 

NodeJS Event Loop파헤치기

안녕하세요! 직방 서비스개발그룹 백엔드팀 아파트파트에서 근무중인 김범준입니다. 이번 포스팅에서는 NodeJS의 Event Loop에 대한 내용을 정리해보려 합니다. 그럼 같이 시작해 볼까요?

medium.com

 

 

이처럼 node.js는 들어올 때와 나갈 때는 싱글 스레드지만, 들어와서 엔진안으로 들어온다면 멀티 스레드 인 것 처럼

작동하고 있습니다.

그렇기 때문에 많은 사람들이 멀티스레드인지 싱글 스레드인지 햇갈려하는 것 같습니다.

 

멀티 스레드의 경우 Thread pool에서 요청을 처리하는 것 처럼 node.js 또한 libuv 내부의 Thread pool에서

마치 멀티 스레드가 동작하는 것 처럼 실행됩니다.

 

그렇다고 싱글스레드가 아니냐?

라고 누군가 묻는다면 결국, 메인 스레드에서 이벤트 루프로 들어오는 요청 자체의 처리는 싱글 스레드가 맞으며

처리된 요청 또한

비동기 작업이 완료되면 해당 작업에 대한 콜백 함수가 이벤트 큐에 추가되고, 메인 스레드를 통해 응답이 클라이언트로 전달되기 때문에 Req와 Res 자체는 싱글 스레드가 맞다고 할 수 있습니다.

 

Comments