😮 문제상황
nest에서 cron을 통해서 스케줄링 작업을 하려하는데 한 스케줄러가 여러번 실행되었습니다. 추적을 해보니 microService객체도 스케줄링을 실행하고 있었습니다. 저의 경우에는 mqtt와 redis를 microservice로 쓰고 있었는데요. cron작업을 했을 때 3개가 돌아갔습니다. cron작업을 1개만 실행하기 위한 해결책을 공유하기 위한 글입니다.
이를 해결하기 위한 방법을 찾아보니 여러개가 있었습니다.
- 스케줄러만 동작시키는 분리된 nest 프로세스를 만들기
- cron job을 백그라운드로 돌리는 api를 만들기
- Buil Queue(?)라는 것을 사용해서 프로세서를 핸들링하기
- db를 locking 해서 스케줄러의 영향을 줄이기
저는 1번을 선택해서 스케줄러만 동작시키는 nest 컨테이너를 만들어보겠습니다.
main.ts
import process from "process";
async function bootstrap() {
// 1. http서버로 사용
const app = await NestFactory.create(AppModule);
// 2. mqtt서버로 사용
console.log(process.env.MQTT_HOST, process.env.MQTT_PORT);
const mqttApp = await NestFactory.createMicroservice<MicroserviceOptions>(
AppModule,
{
transport: Transport.MQTT,
options: {
url: `mqtt://${process.env.MQTT_HOST}:${process.env.MQTT_PORT}`,
},
},
);
// 3. redis서버로 사용
const redisApp = await NestFactory.createMicroservice<MicroserviceOptions>(
AppModule,
{
transport: Transport.REDIS,
options: {
host: `${process.env.REDIS_HOST}`,
port: +process.env.REDIS_PORT,
},
},
);
const port = process.env.PORT || 3000;
// cors 설정
app.enableCors();
await mqttApp.listen();
await redisApp.listen();
await app.listen(port);
}
기존의 저의 main.ts 파일입니다.
worker라는 스케줄러만 돌리는 모듈을 만들고 worker모듈로 새로운 nestFactory를 만들것입니다.
🟩 스케줄러 생성하기
worker.module.ts
import { Module } from '@nestjs/common';
import { WorkerService } from './worker.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Amr } from '../amr/entities/amr.entity';
import { AmrCharger } from '../amr-charger/entities/amr-charger.entity';
import { AmrChargeHistory } from '../amr-charge-history/entities/amr-charge-history.entity';
import { Hacs } from '../hacs/entities/hacs.entity';
import { MqttModule } from '../mqtt.module';
import { ConfigModule } from '@nestjs/config';
import { mssqlConfig, postgresConfig } from '../config/db.config';
import { ScheduleModule } from '@nestjs/schedule';
import { HacsModule } from '../hacs/hacs.module';
import { AmrService } from '../amr/amr.service';
@Module({
imports: [
//env 파일 사용
ConfigModule.forRoot({
isGlobal: true, // 전역으로 사용하기
}),
// // DB 연결
// PostgreSQL 연결 설정
TypeOrmModule.forRootAsync({
useFactory: async () => {
return postgresConfig;
},
}),
// MSSQL 연결 설정
TypeOrmModule.forRootAsync({
name: 'mssqlDB',
useFactory: async () => {
return mssqlConfig;
},
}),
// mqtt 모듈설정
MqttModule,
// schedule 모듈 설정
ScheduleModule.forRoot(),
HModule,
WorkerModule,
TypeOrmModule.forFeature([A, B, C]),
TypeOrmModule.forFeature([H], 'mssqlDB'),
],
providers: [WorkerService, AService],
})
export class WorkerModule {}
app.module.ts에서와 마찬가디로 db연결을 합니다. 그리고 다른 microService모듈들도 import 합니다. scheduler 모듈도 import가 필요합니다.
그리고 worder.service.ts에서 사용할 모듈들도 import 합니다. 스케줄러에서 동작할 service들을 가지고 오는 것입니다.
worker.service.ts
import { Injectable } from '@nestjs/common';
import { Cron } from '@nestjs/schedule';
import { AService } from '../a/a.service';
@Injectable()
export class WorkerService {
constructor(private readonly aService: AService) {}
@Cron('*/10 * * * * *', {
name: 'aCronJobTest',
timeZone: 'Asia/Seoul',
})
InitialScheduler() {
const min = 5;
const max = 10;
const randomInRange = Math.random() * (max - min) + min; // min 이상 max 미만의 난수를 반환
console.log(randomInRange0)
// this.aService.createAByMssql(); [원하는 service]
}
}
예시로 10초마다 동작하는 cron을 만들어봤습니다. aService의 createAByMssql이라는 것을 동작태스트 하기 전에 random 변수를 출력시키는 것이 있습니다. 태스트를 해보고 원하는 service로 교채하면 됩니다.
🟩 스케줄러 적용하기
main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { ResponseInterceptor } from './lib/interceptor/response.interceptor';
import { HttpExceptionFilter } from './lib/filter/httpExceptionFilter';
import { TypeOrmExceptionFilter } from './lib/filter/typeOrmException.filter';
import { ValidationPipe } from '@nestjs/common';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import * as process from 'process';
import { WorkerModule } from './worker/worker.module';
declare const module: any;
async function bootstrap() {
// 1. http서버로 사용
const app = await NestFactory.create(AppModule);
// 2. mqtt서버로 사용
const mqttApp = await NestFactory.createMicroservice<MicroserviceOptions>(
AppModule,
{
transport: Transport.MQTT,
// options: { host: 'localhost', port: 1833, url: 'mqtt://localhost:1883' },
options: {
url: `mqtt://${process.env.MQTT_HOST}:${process.env.MQTT_PORT}`,
},
},
);
// 스케줄러 process 생성
const sheduler = await NestFactory.create(WorkerModule);
// 3. redis서버로 사용
const redisApp = await NestFactory.createMicroservice<MicroserviceOptions>(
AppModule,
{
transport: Transport.REDIS,
options: {
host: `${process.env.REDIS_HOST}`,
port: +process.env.REDIS_PORT,
},
},
);
const port = process.env.PORT || 3000;
// cors 설정
app.enableCors();
await mqttApp.listen();
await redisApp.listen();
await app.listen(port);
await sheduler.init(); // 스케줄러 process 적용
}
bootstrap();
const sheduler = await NestFactory.create(WorkerModule); // 스케줄러 process 생성
await sheduler.init(); // 스케줄러 process 적용
스케줄러를 NestFactory.create()로 생성합니다. 아예 새로운 Nest 객체를 만드는 것입니다. 이 객체는 schedule동작만 실행합니다.
결론
여러가지 방법이 있겠지만 저는 그나마 간단한 방법으로 해결했습니다. 그런데 아직 왜 microService를 사용했을 때 cron이 여러번 발생되는지 원인은 모르겠습니다.
참고
'웹 > Nest' 카테고리의 다른 글
Nestjs를 사용할 때 controller에서 400 Validation failed (numeric string is expected) 에러 발생 시 (0) | 2024.01.16 |
---|---|
[Nest] nest에서 redis 연결하기 (0) | 2023.10.12 |
[Nestjs] @ManyToMany를 OneToMany2개로 쪼개고 select 하기 (0) | 2023.07.30 |
[Nest] Nest.v10 출시 / nest 에서 swc(Speedy Web Compiler) 적용하기 (0) | 2023.07.10 |
[Nest] nest에서 MicroService 세팅하기(mqtt 세팅하기) (0) | 2023.07.04 |