티스토리 뷰

BACKEND/NODEJS

nodejs 쓰레드 활용하기

나를찾는아이 2022. 8. 8. 12:48
728x90
반응형

https://nodejs.org/en/docs/guides/dont-block-the-event-loop/

 

Don't Block the Event Loop (or the Worker Pool) | Node.js

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

nodejs.org

 

nodejs는 non blocking 이벤트 루프 형태의 시스템을 가지고 있습니다

 

이런 nodejs의 장점을 살리기 위해서는 메인 이벤트 루프를 blocking 하는 것을 가급적이면 최소화하는것이 시스템의 최고 효율을 낼수 있습니다

 

메인이벤트루프를 blocking 하는 작업을 실행하면 다른 요청이 들어왔을때 blocking된 작업을 완전히 처리하고 난 뒤에 다음 이벤트가 처리될수 있기때문에 전체적인 시스템의 성능이 저하됩니다

 

메인 이벤트 루프가 단일쓰레드라 nodejs가 싱글스레드라고 단순히 인식하기 쉬운데요

 

메인 이벤트 루프는 싱글스레드로 동작하지만,

 

nodejs는 비싼 작업을 위해 워커 사용하는데 이 워커는 쓰레드풀을 활용합니다

 

 

워커를 사용하는 값비싼 작업은 다음과 같습니다

 

I/O-intensive
DNS: dns.lookup(), dns.lookupService().
File System: All file system APIs except fs.FSWatcher() and those that are explicitly synchronous use libuv's threadpool.
CPU-intensive
Crypto: crypto.pbkdf2(), crypto.scrypt(), crypto.randomBytes(), crypto.randomFill(), crypto.generateKeyPair().
Zlib: All zlib APIs except those that are explicitly synchronous use libuv's threadpool.

 

 

그렇기 때문에 위에서 언급된 i/o, cpu intensive한 작업이 있는 서비스를 운영할 경우에는

 

쓰레드풀의 크기를 늘려 워커를 늘리는것은 서비스의 전체적인 퍼포먼스 향상에 도움을 줍니다

 

 

한번 테스트 해볼까요

 

제 맥북(맥북프로 m1)에서 테스트를 실행해보겠습니다

 

const crypto = require("crypto");

console.time("thread");

for (let i = 1; i <= 16; i++) {
  console.time(`pbkdf2:${i}`);
  crypto.pbkdf2("password", "salt", 100000, 512, "sha512", () => {
    console.timeEnd(`pbkdf2:${i}`);
  });
}

console.timeEnd("thread");

 

값비싼 예제로 언급되었던 crypto.pbkdf2를 사용하여 암호화를 하는 작업을 해보겠습니다

 

이작업은 1번의 작업만 했을때 약 400ms의 시간이 소요되는 작업입니다

 

16번의 pdkdf2 작업을 하는 프로그램을 작성해보았습니다

 

시작시간과 종료시간을 체크하여 각각의 작업이 언제 끝나는지 확인해보죠

 

 

싱글쓰레드의 nodejs를 생각하면 언듯 생각했을때는

 

1번작업이 끝나면 2번작업이 시작되고 2번작업이 끝나면 3번작업이 시작되는 그런 모습을 생각하셨을수 있습니다

 

그래서 16개의 모든 암호화 작업이 끝나는 시간이 최종적으로 

 

약 400ms X 16개 = 약 6.4초

 

이렇게 될것으로 예상하는데요

 

 

프로그램을 실행해보겠습니다

 

 

 

4개단위씩 묶어서 숫자가 400ms씩 증가하는것을 볼수 있습니다

 

4개 단위안에서는 종료시간의 차이가 거의 없습니다

 

왜 4개씩 차이가 나는걸까요?

 

이는 4개의 워커가 동작하고 있어서 순차적으로 들어온 작업을 4개의 워커가 나눠서 작업을 했기 때문에

 

4개의 워커가 작업이 끝나고 다음 4개가 작업이 착수가 된것이라고 볼수 있습니다

 

 

이런식의 느낌으로 말이죠

 

 

그렇다면 워커의 갯수는 어떻게 늘릴수 있을까요?

 

UV_THREADPOOL_SIZE 를 프로그램이 실행되기 직전에 선언해줍니다

 

UV_THREADPOOL_SIZE=8 node index.js

 

 

 

쓰레드를 8개로 늘렸더니 8개 작업마다 시간이 400ms 증가했습니다

 

 

그렇다면 1개로 변경해보고 실행해보죠

 

UV_THREADPOOL_SIZE=1 node index.js

 

최초에 우리가 생각했던 그러한 시간의 모습이 나왔습니다

 

약 400ms씩 증가하여 하나씩 순차적으로 완료가 되고 있습니다

 

 

그림으로 보자면 이런 느낌이겠죠

 

 

 

이렇듯 nodejs는 쓰레드풀을 사용하여 워커를 사용하고 있습니다

 

http://docs.libuv.org/en/v1.x/threadpool.html

 

Thread pool work scheduling — libuv documentation

Thread pool work scheduling libuv provides a threadpool which can be used to run user code and get notified in the loop thread. This thread pool is internally used to run all file system operations, as well as getaddrinfo and getnameinfo requests. Its defa

docs.libuv.org

 

기본사이즈는 4고 UV_THREADPOOL_SIZE 환경변수를 세팅하여 최대 1024개까지 설정이 가능합니다

(Changed in version 1.30.0: the maximum UV_THREADPOOL_SIZE allowed was increased from 128 to 1024.)

 

 

 

 

 

 

 

 

 

 

 

 

728x90
반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함