Пошук уроків, статей та іншого контенту
Створите мікросервіс у NestJS і розберете взаємодію сервісів через транспортний шар.
Мікросервіс — це окремий застосунок, який відповідає за конкретну частину функціональності. На відміну від звичайного HTTP-сервера, мікросервіс може не мати HTTP-маршрутів. Він отримує повідомлення від інших сервісів через транспортний шар.
У NestJS транспортний шар визначає, як сервіси обмінюються повідомленнями. Серед доступних варіантів є:
TCP;
Redis;
NATS;
MQTT;
RabbitMQ;
Kafka;
gRPC.
У цьому уроці використаємо TCP, оскільки його просто запустити локально без додаткових серверів.
Створимо два застосунки:
api-gateway — HTTP-застосунок, який приймає запит від клієнта.
notification-service — мікросервіс, який отримує повідомлення через TCP і обробляє його.
Схема взаємодії:
HTTP-клієнт
|
| POST /notifications
v
api-gateway
|
| TCP-повідомлення
v
notification-serviceПереконайтеся, що Nest CLI встановлений:
npm install -g @nestjs/cliСтворіть два окремі NestJS-застосунки:
nest new api-gateway
nest new notification-serviceВстановіть пакет для роботи з мікросервісами в обох проєктах:
cd api-gateway
npm install @nestjs/microservices
cd ../notification-service
npm install @nestjs/microservicesПерейдіть у папку notification-service і замініть вміст файлу src/main.ts:
import { NestFactory } from '@nestjs/core';
import { Transport } from '@nestjs/microservices';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.createMicroservice(AppModule, {
transport: Transport.TCP,
options: {
host: '127.0.0.1',
port: 8877,
},
});
await app.listen();
console.log('Notification microservice is running on TCP port 8877');
}
bootstrap();NestFactory.createMicroservice створює застосунок, який працює як мікросервіс.
У конфігурації:
Transport.TCP визначає транспорт;
host — адресу, на якій мікросервіс очікує з’єднання;
port — TCP-порт мікросервісу.
Цей застосунок не запускає HTTP-сервер. Він очікує повідомлення через TCP.
Замініть вміст файлу src/app.controller.ts:
import { Controller } from '@nestjs/common';
import { MessagePattern, Payload } from '@nestjs/microservices';
interface NotificationMessage {
recipient: string;
text: string;
}
@Controller()
export class AppController {
@MessagePattern({ cmd: 'send_notification' })
sendNotification(@Payload() message: NotificationMessage) {
console.log(
`Надсилання повідомлення користувачу ${message.recipient}: ${message.text}`,
);
return {
success: true,
recipient: message.recipient,
};
}
}Декоратор @MessagePattern визначає шаблон повідомлення, який обробляє метод.
У цьому прикладі мікросервіс реагує на такий шаблон:
{ cmd: 'send_notification' }Коли інший сервіс надішле повідомлення з цим шаблоном, NestJS викличе метод sendNotification.
Параметр @Payload() містить дані повідомлення:
{
recipient: 'user@example.com',
text: 'Ваше замовлення готове'
}Значення, повернуте методом, буде відправлене назад сервісу-клієнту.
Файл src/app.module.ts може залишитися стандартним:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}AppService у цьому прикладі не використовується, але його можна залишити в модулі.
Запустіть мікросервіс:
npm run start:devУ консолі має з’явитися повідомлення про запуск на TCP-порті 8877.
Тепер налаштуємо api-gateway. Він прийматиме HTTP-запит і пересилатиме його мікросервісу через TCP.
Замініть вміст файлу api-gateway/src/app.module.ts:
import { Module } from '@nestjs/common';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { AppController } from './app.controller';
@Module({
imports: [
ClientsModule.register([
{
name: 'NOTIFICATION_SERVICE',
transport: Transport.TCP,
options: {
host: '127.0.0.1',
port: 8877,
},
},
]),
],
controllers: [AppController],
})
export class AppModule {}ClientsModule.register створює клієнта для підключення до мікросервісу.
Властивість name — це токен, через який клієнт буде доступний у контролері:
'NOTIFICATION_SERVICE'Параметри host, port і transport мають відповідати налаштуванням мікросервісу.
Замініть вміст файлу api-gateway/src/app.controller.ts:
import { Body, Controller, Post } from '@nestjs/common';
import { ClientProxy } from '@nestjs/microservices';
import { Inject } from '@nestjs/common';
import { firstValueFrom } from 'rxjs';
interface CreateNotificationDto {
recipient: string;
text: string;
}
@Controller('notifications')
export class AppController {
constructor(
@Inject('NOTIFICATION_SERVICE')
private readonly notificationClient: ClientProxy,
) {}
@Post()
async createNotification(@Body() body: CreateNotificationDto) {
const response = await firstValueFrom(
this.notificationClient.send(
{ cmd: 'send_notification' },
body,
),
);
return response;
}
}Розглянемо основні частини цього коду:
ClientProxy — об’єкт для взаємодії з мікросервісом;
@Inject('NOTIFICATION_SERVICE') отримує клієнта, зареєстрованого в AppModule;
send() надсилає повідомлення і очікує відповідь;
перший аргумент send() — шаблон повідомлення;
другий аргумент — дані повідомлення;
firstValueFrom() перетворює Observable на Promise.
Метод send() має відповідати шаблону, який обробляє мікросервіс:
{ cmd: 'send_notification' }Якщо шаблони не збігаються, NestJS не знайде відповідного обробника.
Файл api-gateway/src/main.ts може залишитися стандартним:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
console.log('API gateway is running on http://localhost:3000');
}
bootstrap();Запустіть gateway в іншому терміналі:
cd api-gateway
npm run start:devКоли обидва застосунки запущені, надішліть HTTP-запит до gateway:
curl -X POST http://localhost:3000/notifications \
-H "Content-Type: application/json" \
-d '{"recipient":"user@example.com","text":"Ваше замовлення готове"}'Послідовність виконання буде такою:
Gateway отримає POST-запит.
Контролер gateway створить TCP-повідомлення.
Повідомлення буде надіслане на порт 8877.
Мікросервіс знайде обробник із шаблоном send_notification.
Метод sendNotification обробить дані.
Результат повернеться до gateway.
Gateway поверне результат HTTP-клієнту.
Очікувана відповідь:
{
"success": true,
"recipient": "user@example.com"
}У терміналі notification-service з’явиться повідомлення:
Надсилання повідомлення користувачу user@example.com: Ваше замовлення готовеПовідомлення складається з двох логічних частин:
this.notificationClient.send(
{ cmd: 'send_notification' },
{
recipient: 'user@example.com',
text: 'Ваше замовлення готове',
},
);Перша частина — шаблон:
{ cmd: 'send_notification' }Він визначає, який обробник має бути викликаний.
Друга частина — корисне навантаження:
{
recipient: 'user@example.com',
text: 'Ваше замовлення готове',
}Вона містить дані для обробки.
Шаблон може містити не лише cmd, але для початкового прикладу достатньо використовувати простий об’єкт із командою.
send і очікування відповідіМетод send() використовується для взаємодії у стилі «запит-відповідь»:
const response = await firstValueFrom(
client.send({ cmd: 'some_command' }, data),
);Мікросервіс повинен повернути результат з обробника:
@MessagePattern({ cmd: 'some_command' })
handleMessage(data: unknown) {
return {
success: true,
};
}Якщо мікросервіс не поверне значення, клієнт не отримає корисної відповіді.
send() повертає Observable, тому в асинхронному методі часто використовують:
import { firstValueFrom } from 'rxjs';і:
const result = await firstValueFrom(observable);Якщо gateway працює, але notification-service не запущений, підключення через TCP не відбудеться.
Спочатку запустіть:
cd notification-service
npm run start:devПотім запустіть gateway.
Порт у мікросервісі:
port: 8877має збігатися з портом у клієнті gateway:
port: 8877Якщо змінити порт в одному місці, його потрібно змінити і в іншому.
Ці шаблони різні:
{ cmd: 'send_notification' }{ cmd: 'sendNotification' }Назва команди повинна точно збігатися у клієнті та мікросервісі.
@MessagePatternМікросервіс із TCP-транспортом обробляє повідомлення через @MessagePattern:
@MessagePattern({ cmd: 'send_notification' })@Get() і @Post() призначені для HTTP-контролерів, а не для TCP-повідомлень.
Якщо клієнт не зареєстрований через ClientsModule.register, NestJS не зможе його впровадити в контролер.
У модулі має бути конфігурація:
ClientsModule.register([
{
name: 'NOTIFICATION_SERVICE',
transport: Transport.TCP,
options: {
host: '127.0.0.1',
port: 8877,
},
},
]);А токен у @Inject() має бути таким самим:
@Inject('NOTIFICATION_SERVICE')Мікросервіс у NestJS — це окремий застосунок, який може взаємодіяти з іншими сервісами через транспортний шар.
Transport.TCP дає змогу обмінюватися повідомленнями через TCP.
createMicroservice() створює NestJS-застосунок у режимі мікросервісу.
@MessagePattern() визначає, які повідомлення обробляє мікросервіс.
ClientProxy використовується для надсилання повідомлень.
ClientsModule.register() реєструє клієнта в модулі.
send() використовується для запиту, який очікує відповідь.
Шаблон повідомлення в send() має точно збігатися з шаблоном у @MessagePattern().
Gateway може приймати HTTP-запити і передавати обробку окремому мікросервісу.