Basics

Basics

Nest 마이크로 서비스는 HTTP와 다른 전송 계층을 사용하는 응용 프로그램 유형입니다.

Installation

먼저 필요한 패키지를 설치해야 합니다.

$ npm i --save @nestjs/microservices

Overview

일반적으로 Nest는 여러 내장 전송기를 지원합니다. 요청-응답이벤트 기반 패러다임을 기반으로 하며 전체 통신 로직이 추상화 계층 뒤에 숨겨져 있습니다. 이를 통해 코드 라인을 변경하지 않고도 운송업체(transporter)간에 쉽게 전환 할 수 있습니다. 그러나 요청-응답 패러다임은 다양한 범위의 문제를 해결하도록 설계된 Kafka 또는 NATS 스트리밍과 같은 로그 기반 지속성이 제공되는 스트리밍 플랫폼에서는 그다지 의미가 없습니다. 그럼에도 불구하고 이벤트 기반 (단방향) 통신 또는 응용 프로그램 컨텍스트 기능과 함께 사용할 수 있습니다.

Getting started

마이크로 서비스를 생성하기 위해 우리는 NestFactory 클래스의 createMicroservice()메소드를 사용합니다.

@@filename(main)
import { NestFactory } from '@nestjs/core';
import { Transport } from '@nestjs/microservices';
import { ApplicationModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.createMicroservice(ApplicationModule, {
    transport: Transport.TCP,
  });
  app.listen(() => console.log('Microservice is listening'));
}
bootstrap();

info 힌트 마이크로 서비스는 기본적으로 TCP 프로토콜을 통해 메시지를 듣고 있습니다.

createMicroservice()메소드의 두 번째 인수는 옵션 객체입니다. 이 객체에는 두 멤버가 있을 수 있습니다.

transport

Specifies the transporter (for example, Transport.NATS)

options

트랜스포터 동작을 결정하는 트랜스포터별 옵션 객체

The 옵션 개체는 선택한 트랜스포터에 따라 다릅니다. TCP 트랜스포터는 아래에 설명된 몇가지 속성을 가져옵니다.

host

연결 호스트 이름

port

연결 포트

retryAttempts

총 연결 시도 횟수

retryDelay

연결 재시도 지연(ms)

Patterns

마이크로 서비스는 패턴을 통해 메시지와 이벤트를 모두 인식합니다. 패턴은 리터럴 객체 또는 문자열과 같은 일반 값입니다. 결국 모든 패턴이 직렬화되므로 데이터와 함께 네트워크를 통해 전송될 수 있습니다. 따라서, 리시버는 들어오는 메시지를 대응하는 핸들러와 쉽게 연관시킬 수있다.

Request-response

요청-응답 통신 메커니즘은 다양한 외부 서비스간에 메시지를 교환해야 할 때 유용합니다. 또한 이 패러다임을 사용하면 서비스가 실제로 메시지를 수신했는지 확인할 수 있습니다.

서비스가 네트워크를 통해 데이터를 교환할 수 있도록 Nest는 두 채널을 생성합니다. 하나는 데이터 전송을 담당하고 다른 하나는 들어오는 응답을 수신합니다. 그러나 항상 그런 것은 아닙니다. 예를 들어 NATS와 같은 플랫폼은 이러한 기능을 기본적으로 제공하므로 자체적으로 수행할 필요가 없습니다.

기본적으로, 요청-응답 패러다임을 기반으로 하는 메시지 핸들러를 만들기 위해 우리는 @nestjs/microservices 패키지에서 가져온 @MessagePattern()데코레이터를 사용합니다.

@@filename(math.controller)
import { Controller } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices';

@Controller()
export class MathController {
  @MessagePattern({ cmd: 'sum' })
  accumulate(data: number[]): number {
    return (data || []).reduce((a, b) => a + b);
  }
}

accumulate() 핸들러는 cmd:'sum' 패턴을 충족시키는 메시지를 듣고 있습니다. 패턴 핸들러는 클라이언트로부터 전달된 data라는 단일 인수를 취합니다. 이 경우, 데이터는 누적되어야 하는 숫자의 배열입니다.

Asynchronous responses

각 메시지 핸들러는 동기식 또는 비동기식으로 응답할 수 있습니다. 따라서 async 메소드가 지원됩니다.

@@filename()
@MessagePattern({ cmd: 'sum' })
async accumulate(data: number[]): Promise<number> {
  return (data || []).reduce((a, b) => a + b);
}

또한 Observable을 반환할 수 있으므로 스트림이 완료 될 때까지 값이 방출됩니다.

@@filename()
@MessagePattern({ cmd: 'sum' })
accumulate(data: number[]): Observable<number> {
  return from([1, 2, 3]);
}

위의 메시지 처리기는 배열의 각 항목과 함께 3 번 응답합니다.

Event-based

요청-응답 방법은 서비스간에 메시지를 지속적으로 교환해야 할 때 유용하지만 응답을 기다리지 않고 이벤트를 게시하려고 할 때 완전히 쓸모없는 불필요한 오버 헤드가 너무 많이 발생합니다. 예를 들어, 시스템의 이 부분에서 특정 상황이 발생했음을 다른 서비스에 알리려고 합니다. 따라서, 이벤트 기반 통신도 지원합니다.

이벤트 핸들러를 만들기 위해 우리는@nestjs/microservices 패키지에서 가져온 @EventPattern ()데코레이터를 사용합니다.

@@filename()
@EventPattern('user_created')
async handleUserCreated(data: Record<string, unknown>) {
  // business logic
}

handleUserCreated()메소드는 user_created 이벤트를 청취하고 있습니다. 이벤트 핸들러는 클라이언트로부터 전달 된 data라는 단일 인수를 받습니다 (이 경우 네트워크를 통해 전송된 이벤트 페이로드).

Client

메시지를 교환하거나 Nest 마이크로 서비스에 이벤트를 게시하기 위해 몇가지 방법으로 생성할 수 있는 ClientProxy클래스를 사용합니다. 먼저 정적 register()메소드를 노출하는 ClientsModule을 가져올 수 있습니다. 이 메소드는 모든 요소가 name (마이크로 서비스 식별자의 일종)과 마이크로 서비스 특정 옵션 (이것이createMicroservice() 방법에 전달된 것과 동일한 객체)을 갖는 매개 변수로 배열을 취합니다.

ClientsModule.register([
  { name: 'MATH_SERVICE', transport: Transport.TCP },
]),

info 힌트 ClientsModule@nestjs/microservices 패키지에서 가져옵니다.

모듈을 가져 오면 @Inject()데코레이터를 사용하여 MATH_SERVICE를 주입할 수 있습니다.

constructor(
  constructor(
    @Inject('MATH_SERVICE') private readonly client: ClientProxy,
  ) {}
)

info 힌트 ClientProxy 클래스는@nestjs/microservices 패키지에서 가져옵니다.

그럼에도 불구하고,이 접근법은 마이크로 서비스 구성을 비동기적으로 가져올 수 없습니다. 이 경우에, 우리는 클라이언트 인스턴스 인 Custom provider를 등록하기 위해 ClientProxyFactory를 직접 사용할 수 있습니다 :

{
  provide: 'MATH_SERVICE',
  useFactory: (configService: ConfigService) => {
    const mathSvcOptions = configService.getMathSvcOptions();
    return ClientProxyFactory.create(mathSvcOptions);
  },
  inject: [ConfigService],
}

info 힌트 ClientProxyFactory@nestjs/microservices 패키지에서 가져옵니다.

마지막으로 가능한 해결책은 @Client()속성 데코레이터를 사용하는 것입니다.

@Client({ transport: Transport.TCP })
client: ClientProxy;

info 힌트 @Client()@nestjs/microservices 패키지에서 가져옵니다.

그러나 데코레이터를 사용하는 것은 권장되지 않습니다 (테스트하기 어렵고 클라이언트 인스턴스를 공유하기가 어렵습니다).

ClientProxy게으른입니다. 즉시 연결을 시작하지 않습니다. 대신, 첫 번째 마이크로 서비스 호출 전에 설정되고 이후의 각 호출에서 재사용됩니다. 그러나 애플리케이션 부트 스트랩 프로세스를 지연시키고 연결을 수동으로 초기화하려면 OnModuleInit 라이프 사이클 후크 내에서 connect()메소드를 사용할 수 있습니다.

@@filename()
async onModuleInit() {
  await this.client.connect();
}

연결을 만들 수 없으면 connect()메소드는 해당 오류 객체와 함께 거부됩니다.

Sending messages

ClientProxysend()메소드를 노출시킵니다. 이 메소드는 마이크로 서비스를 호출하기 위한 것이며 응답과 함께 Observable을 반환합니다. 따라서 방출된 값을 쉽게 가입할 수 있습니다.

@@filename()
accumulate(): Observable<number> {
  const pattern = { cmd: 'sum' };
  const payload = [1, 2, 3];
  return this.client.send<number>(pattern, payload);
}

send()메소드는 patternpayload라는 두 개의 인수를 취합니다. pattern@MessagePattern() 데코레이터에 정의된 것과 동일해야 하며, payload는 다른 마이크로 서비스로 전송하려는 메시지입니다.

Publishing events

또 다른 방법은 emit()입니다. 이 방법의 책임은 이벤트를 메시지 브로커에 공개하는 것입니다.

@@filename()
async publish() {
  this.client.emit<number>('user_created', new UserCreatedEvent());
}

emit()메소드는 patternpayload라는 두 개의 인수를 취합니다. pattern@EventPattern() 데코레이터에 정의된 것과 동일해야하며, payload는 다른 마이크로 서비스로 전송하려는 이벤트 페이로드입니다.

Last updated