node.js의 이벤트 루프는 싱글스레드에서 돌아간다. node.js 서버에서 멀티코어 CPU를 제대로 활용하기 위해서는 클러스터 모듈을 사용해 프로세스를 포크하여 실행해야 한다.
node.js 클러스터는 기본적으로 윈도우즈를 제외하고 라운드로빈 방식으로 스케쥴링 한다.
윈도우즈에서는 기본적으로 OS가 직접 스케쥴링 하지만, libuv가 IOCP 처리를 오버헤드 없이 잘 분산할 수 있다면 라운드로빈으로 바뀐다.
CPU 코어 수만큼 워커를 생성하는 예
import cluster from 'cluster';
import { cpus } from 'os';
import http from 'http';
const numCPUs = cpus().length;
if (cluster.isPrimary) {
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
} else {
http.createServer((req, res) => {
res.writeHead(200);
res.end('hello world\n');
}).listen(8080);
}
마스터는 워커들을 관리해야 하기에 워커를 생성 및 관리하는 기능 등만 넣는 것이 좋다.
아래와 같이 스케쥴링 방식을 직접 변경할 수 있다.
// 라운드로빈 방식
cluster.schedulingPolicy = cluster.SCHED_RR
// OS가 직접 스케쥴링
cluster.schedulingPolicy = cluster.SCHED_NONE
워커는 fork한 만큼 생성되고, 다음과 같이 현재 스레드가 마스터인지 아닌지 확인할 수 있다.
let worker = cluster.fork();
if (cluster.isPrimary) {
// primary
} else if (cluster.isWorker) {
// none primary
}
cluster.isMaster 는 deprecate 되고 cluster.isPrimary에 통합되었다.
클러스터 이벤트 리스너를 아래처럼 사용할 수 있다.
// 워커가 죽으면 새로운 워커를 포크하는 함수
cluster.on('exit', (worker, code, signal) => {
console.log('worker %d died (%s). restarting...',
worker.process.pid, signal || code);
cluster.fork();
});
클러스터 이벤트는 disconnect, exit, fork, listening, message, online, setup 등이 있다.
pm2의 클러스터 모드를 사용하면 클러스터 생명주기를 개발자가 직접 관리하지 않아도 된다. pm2 공식 문서에 따르면 클러스터 모드는 node.js의 클러스터 모듈을 사용해 구현했다고 한다.
참고