RabbitMQ — servislar orasida xabar almashish

~25 daq · NestJS · Ilg'or mavzular

Muammodan boshlaylik

Orders Service buyurtma yaratdi. Notifications Service ga email yuborishi kerak. Lekin ular alohida ilovalar — bir-birini to'g'ridan-to'g'ri chaqira olmaydi.

HTTP bilan to'g'ridan-to'g'ri chaqirish mumkin — lekin muammo bor:

Orders Service  ──HTTP──►  Notifications Service
                               ↑ ishlamayapti!

Notifications Service vaqtincha ishlamasa — buyurtma ham yaratilmaydi. Ikkisi bir-biriga bog'liq bo'lib qoldi.

Yechim: Message Queue. Xabarni qoldiring — Notifications Service o'zi kelganda olsin.


RabbitMQ nima?

RabbitMQ — servislar orasida xabar almashish uchun navbat tizimi (message broker).

Bir servis xabar yozib navbatga tashlaydi — ikkinchi servis tayyor bo'lganda navbatdan oladi.


Hayotiy o'xshatma

Pochta qutisi. Siz xat yozib pochta qutisiga solasiz. Qabul qiluvchi uyda bo'lmasa ham — xat saqlanib qoladi. U qaytganda xatni oladi. Siz uni kutib o'tirmagansiz.

Orders ──► [pochta qutiси / RabbitMQ] ──► Notifications
           xabar saqlanib turadi

RabbitMQ ni qanday ishga tushirish?

Eng sodda yo'l — CloudAMQP (bepul, o'rnatish shart emas):

  1. cloudamqp.com ga kiring
  2. "Create New Instance" → "Little Lemur" (bepul tarif)
  3. Dastur nomi va region tanlang
  4. "Details" da URL ni nusxalang — shunday ko'rinadi:
amqps://abcdef:XyZ123@moose.rmq.cloudamqp.com/abcdef

Shu URL — RabbitMQ serveringiz manzili. Docker yoki o'rnatish kerak emas.


O'rnatish

npm install @nestjs/microservices amqplib amqp-connection-manager

Loyiha tuzilmasi

Bu yerda ikkita alohida NestJS ilova ishlatamiz:

myapp/
  orders-service/              ← buyurtmalar (xabar yuboradi)
    src/
      orders/
        orders.module.ts
        orders.service.ts
      app.module.ts
      main.ts
      .env

  notifications-service/       ← bildirishnomalar (xabar qabul qiladi)
    src/
      notifications/
        notifications.controller.ts
        notifications.module.ts
      app.module.ts
      main.ts
      .env

.env fayllar

# orders-service/.env
RABBITMQ_URL=amqps://abcdef:XyZ123@moose.rmq.cloudamqp.com/abcdef
# notifications-service/.env
RABBITMQ_URL=amqps://abcdef:XyZ123@moose.rmq.cloudamqp.com/abcdef

Consumer — xabarlarni qabul qiluvchi

Notifications Service navbatni tinglaydi:

// notifications-service/src/main.ts
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, {
    transport: Transport.RMQ,
    options: {
      urls: [process.env.RABBITMQ_URL],
      queue: 'notifications_queue', // ← shu navbatni tinglaydi
    },
  });

  await app.listen();
  console.log('Notifications service ishga tushdi');
}
bootstrap();
// notifications-service/src/notifications/notifications.controller.ts
import { Controller } from '@nestjs/common';
import { EventPattern, Payload } from '@nestjs/microservices';

@Controller()
export class NotificationsController {

  @EventPattern('order.created') // ← shu xabar kelganda ishlaydi
  async handleOrderCreated(@Payload() data: { email: string; orderId: string }) {
    console.log(`Email yuborilmoqda: ${data.email}`);
    // shu yerda haqiqiy email yuborish
  }
}

Producer — xabar yuboruvchi

Orders Service navbatga xabar qoldiradi:

// orders-service/src/orders/orders.module.ts
import { Module } from '@nestjs/common';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { OrdersService } from './orders.service';

@Module({
  imports: [
    ClientsModule.register([
      {
        name: 'NOTIFICATIONS_SERVICE',   // ← inject qilishda shu nom
        transport: Transport.RMQ,
        options: {
          urls: [process.env.RABBITMQ_URL],
          queue: 'notifications_queue',  // ← consumer bilan bir xil bo'lishi shart
        },
      },
    ]),
  ],
  providers: [OrdersService],
})
export class OrdersModule {}
// orders-service/src/orders/orders.service.ts
import { Inject, Injectable } from '@nestjs/common';
import { ClientProxy } from '@nestjs/microservices';

@Injectable()
export class OrdersService {
  constructor(
    @Inject('NOTIFICATIONS_SERVICE') private notifyClient: ClientProxy,
  ) {}

  async createOrder(dto: { email: string; product: string }) {
    const order = { id: Date.now(), ...dto };

    // Navbatga xabar qoldiramiz — javob kutmaymiz
    this.notifyClient.emit('order.created', {
      email: dto.email,
      orderId: order.id,
    });

    return order; // ← darhol qaytadi, Notifications Service kutilmaydi
  }
}

Nima uchun emit, nima uchun send?

// emit — bir tomonlama (pochta qutisi)
// Xabar yuborasiz, javob kutmaysiz
this.notifyClient.emit('order.created', data);

// send — ikki tomonlama (telefon)
// Javob kutiladi
const result = await firstValueFrom(
  this.inventoryClient.send('check.stock', { productId: 1 })
);

Consumer da ham shunga mos:

// emit uchun — javob qaytarmaydi
@EventPattern('order.created')
handleOrder(@Payload() data: any) { ... }

// send uchun — javob qaytaradi
@MessagePattern('check.stock')
checkStock(@Payload() data: any) {
  return { available: true }; // ← Orders Service ga qaytadi
}

Tez-tez uchraydigan xatolar

1. Xabar yetkazilmayapti Sababi: Queue nomi producer va consumer da mos kelmayapti. Yechim: Ikkala tomonda queue: 'notifications_queue' aynan bir xil bo'lsin.


2. send() javob bermayapti Sababi: Consumer da @EventPattern ishlatilgan — u javob qaytarmaydi. Yechim: Javob kerak bo'lsa @MessagePattern ishlating.


3. CloudAMQP ga ulanmayapti

Error: connect ECONNREFUSED

Sababi: URL noto'g'ri yoki .env fayl o'qilmayapti. Yechim: console.log(process.env.RABBITMQ_URL) bilan URL ni tekshiring. ConfigModule orqali .env yuklanganini tasdiqlang.


Xulosa

  • RabbitMQ — servislar orasida ishonchli xabar almashish
  • CloudAMQP — o'rnatishsiz, bepul bulutdagi RabbitMQ
  • Producer — notifyClient.emit() bilan navbatga xabar qoldiradi
  • Consumer — @EventPattern() bilan navbatni tinglaydi
  • Queue nomi ikki tomonda ham bir xil bo'lishi shart
  • emit — javobsiz, send — javobli

Keyingi dars: 41-dars: Microservices — HTTP orqali ma'lumot almashish