무비위키 프로젝트의 성능 개선을 위해 cluster 모듈을 이용해 멀티 프로세싱을 적용했다.
멀티 스레드와 멀티 프로세스가 같은 개념인줄 알았는데 다른 개념이었다.
멀티 스레딩
단일 프로세스 내에서 여러 개의 스레드를 생성하여 동시에 작업을 수행하는 것을 의미한다.
각 스레드는 같은 주소 공간을 공유하므로 데이터 공유가 간단하고 효율적이다.
하지만 스레드 간의 동기화와 경쟁 조건 같은 복잡한 문제를 처리해야 하므로 디버깅이 어려울 수 있다.
멀티 프로세싱 또는 클러스터링
여러 개의 독립된 프로세스를 생성하여 각각을 별도의 CPU 코어에서 실행하는 것을 의미한다.
각 프로세스는 자체적인 주소 공간을 가지고 있으며, 데이터 공유와 통신을 위해 별도의 매커니즘이 필요하다.
각 프로세스가 독립적으로 실행되므로 안정성과 격리성이 높지만 프로세스 간의 통신과 데이터 공유에는 추가적인 비용과 복잡성이 따른다.
Multi Processing (Clustering) | Multi Threading | |
구현 | Cluster Module | Worker Thread |
장점 | - CPU 작업과 I/O 작업에 모두 효과적 - 각 프로세스는 독립적으로 실행되어 CPU 자원을 별도로 활용 가능 - I/O 작업에서 발생하는 대기 시간에도 다른 프로세스가 실행될 수 있음 - 각 프로세스는 독립적인 메모리 공간을 가지기 때문에 데이터 공유와 동기화 문제가 적음 - 안정성과 격리성이 높음 - 하나의 프로세스가 실패해도 다른 프로세스는 영향이 없음 |
- CPU 집약적인 작업에 효과적 - 여러 스레드가 동시에 CPU 자원을 활용하여 병렬 처리 가능 |
단점 | - 프로세스 간의 통신과 데이터 공유에는 추가적인 비용과 복잡성이 따름 - 프로세스 간의 컨텍스트 전환 비용이 발생하여 오버헤드가 있을 수 있음 |
- 스레드가 I/O 작업을 수행하면서 대기 시간이 발생하면 다른 스레드도 영향을 받아 효율이 떨어질 수 있음 - 스레드 간에 동기화와 경쟁 조건에 대한 관리가 필요하며, 잘못된 동기화로 인해 예기치 못한 결과가 발생할 수 있음 |
app-cluster.service.ts 파일
cluster를 사용하여 멀티 프로세싱 적용
마스터 프로세스는 주어진 워커 수에 따라 워커 프로세스를 생성하고 관리한다.
워커 프로세스는 실제 작업을 수행한다.
import cluster from 'cluster';
import os from 'os';
import { Injectable } from '@nestjs/common';
// 시스템의 CPU 정보를 가져와 CPU 코어의 개수를 저장
const numCPUs = os.cpus().length;
@Injectable()
export class AppClusterService {
// clusterize 메서드는 주어진 콜백 함수를 실행하며 클러스터를 설정
static clusterize(callback: Function): void {
/*isPrimary 속성을 사용하여 연해 프로세스가 마스터 프로세스인지 확인
마스터 프로세스는 클러스터 생성 및 관리를 담당하고 워커 프로세스는 실제 작업을 수행*/
if(cluster.isPrimary){
console.log(`Master server started on ${process.pid}`);
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died. Restarting`);
cluster.fork();
})
} else {
console.log(`Cluster server started on ${process.pid}`)
callback();
}
}
}
main.ts 파일
import { Logger, ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import config from 'config';
import cookieParser from 'cookie-parser';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './common/exceptions/http-exception-filter';
import { AppClusterService } from './app-cluster.service';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const serverConfig = config.get('server');
const port = serverConfig.port;
app.use(cookieParser());
// class-validator 적용
app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // DTO에 정의되지 않은 프로퍼티 자동 제거
forbidNonWhitelisted: true, // DTO에 정의되지 않은 프로퍼티 요청에 포함 > 요청 거부
transform: true, // 받아온 데이터를 DTO 클래스로 변환해줌
}),
);
// httpExceptionFilter 적용
app.useGlobalFilters(new HttpExceptionFilter());
app.enableCors({
origin: true,
credentials: true,
});
await app.listen(port);
Logger.log(`Application running on port ${port}`);
}
// bootstrap();
AppClusterService.clusterize(bootstrap);
출처 및 참조
https://dev.to/danudenny/clustering-nest-js-2mj7
https://inpa.tistory.com/entry/NODE-%F0%9F%93%9A-workerthreads-%EB%AA%A8%EB%93%88?category=890802
'Development > TIL' 카테고리의 다른 글
스트레스 테스트 (feat. artillery) (0) | 2023.06.24 |
---|---|
Redis Sorted Set (Zset) (0) | 2023.06.21 |
비교 단위별 Diff 및 Patch 코드 동작 속도 측정 (0) | 2023.06.16 |
patch 알고리즘 수정 (feat. </p> 태그 지옥) (0) | 2023.06.16 |
DP, LCS, MED, SES 개념정리 (1) | 2023.06.15 |