IT/NodeJS

Node.js에서 CPU-집약적 작업 처리 전략

KeepGooing 2024. 12. 1. 11:50
반응형

Node.js에서 CPU-집약적 작업 처리 전략

 

1. 워커 스레드(worker threads) 활용

Node.js v10.5.0부터 도입된 워커 스레드는 CPU-집약적 작업을 병렬로 처리할 수 있게 해줍니다.

const { Worker, isMainThread, parentPort } = require('worker_threads');

if (isMainThread) {
    const worker = new Worker(__filename);
    worker.on('message', (result) => {
        console.log('Result from worker:', result);
    });
    worker.postMessage(100); // 계산할 피보나치 수열의 항 번호
} else {
    parentPort.on('message', (n) => {
        const result = fibonacci(n);
        parentPort.postMessage(result);
    });
}

function fibonacci(n) {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}
        

설명: 워커 스레드를 사용하면 메인 스레드를 차단하지 않고 CPU-집약적인 작업을 별도의 스레드에서 실행할 수 있습니다. 이는 애플리케이션의 전반적인 응답성을 유지하는 데 도움이 됩니다.

2. 클러스터 모듈 사용

Node.js의 클러스터 모듈을 사용하면 여러 프로세스에 작업을 분산시킬 수 있습니다.

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    console.log(`마스터 ${process.pid} 실행 중`);

    for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('exit', (worker, code, signal) => {
        console.log(`워커 ${worker.process.pid} 종료`);
    });
} else {
    http.createServer((req, res) => {
        if (req.url === '/compute') {
            const result = heavyComputation();
            res.writeHead(200);
            res.end(`결과: ${result}`);
        } else {
            res.writeHead(200);
            res.end('Hello World');
        }
    }).listen(8000);

    console.log(`워커 ${process.pid} 시작`);
}

function heavyComputation() {
    // CPU-집약적인 작업 시뮬레이션
    let result = 0;
    for (let i = 0; i < 1e9; i++) {
        result += i;
    }
    return result;
}
        

설명: 클러스터 모듈을 사용하면 여러 CPU 코어를 활용하여 작업을 병렬로 처리할 수 있습니다. 이는 특히 웹 서버의 성능을 크게 향상시킬 수 있습니다.

3. 자식 프로세스 생성

child_process 모듈을 사용하여 별도의 프로세스에서 CPU-집약적 작업을 실행할 수 있습니다.

const { exec } = require('child_process');

function runCPUIntensiveTask(data) {
    return new Promise((resolve, reject) => {
        exec(`node cpu-intensive-task.js ${data}`, (error, stdout, stderr) => {
            if (error) {
                reject(error);
                return;
            }
            resolve(stdout.trim());
        });
    });
}

// 사용 예
runCPUIntensiveTask('someData')
    .then(result => console.log('결과:', result))
    .catch(error => console.error('에러:', error));
        

설명: 자식 프로세스를 사용하면 메인 Node.js 프로세스와는 별도로 CPU-집약적 작업을 실행할 수 있습니다. 이 방법은 특히 외부 스크립트나 명령을 실행해야 할 때 유용합니다.

4. 비동기 처리 최적화

CPU-집약적 작업을 작은 단위로 나누어 비동기적으로 처리할 수 있습니다.

function processLargeArray(array, batchSize = 1000) {
    return new Promise((resolve, reject) => {
        const results = [];
        let index = 0;

        function processBatch() {
            const batch = array.slice(index, index + batchSize);
            batch.forEach(item => {
                // CPU-집약적인 작업 수행
                results.push(heavyComputation(item));
            });

            index += batchSize;

            if (index < array.length) {
                setImmediate(processBatch);
            } else {
                resolve(results);
            }
        }

        processBatch();
    });
}

function heavyComputation(item) {
    // CPU-집약적인 작업 시뮬레이션
    let result = 0;
    for (let i = 0; i < 1e6; i++) {
        result += Math.sqrt(item * i);
    }
    return result;
}

// 사용 예
const largeArray = Array.from({ length: 1e5 }, (_, i) => i);
processLargeArray(largeArray)
    .then(results => console.log('처리 완료:', results.length))
    .catch(error => console.error('에러:', error));
        

설명: 대규모 작업을 작은 단위로 나누어 비동기적으로 처리함으로써, 이벤트 루프의 차단을 최소화하고 애플리케이션의 응답성을 유지할 수 있습니다.

결론

Node.js에서 CPU-집약적 작업을 효율적으로 처리하기 위해서는 워커 스레드, 클러스터 모듈, 자식 프로세스, 그리고 비동기 처리 최적화 등 다양한 전략을 활용할 수 있습니다. 각 방법은 상황에 따라 장단점이 있으므로, 애플리케이션의 요구사항과 처리해야 할 작업의 특성을 고려하여 적절한 전략을 선택해야 합니다. 또한, 이러한 기법들을 조합하여 사용하면 더욱 효과적인 성능 최적화를 달성할 수 있습니다. CPU-집약적 작업을 처리할 때는 항상 애플리케이션의 전반적인 성능과 응답성을 고려하고, 필요에 따라 전략을 조정하는 것이 중요합니다.

반응형