Trong bài trước chúng ta đã triển khai mô hình Microservice sử dụng lớp vận chuyển khác với HTTP. Bài trước chúng ta đã sử dụng TCP, xem lại tại đây.

Ngoài TCP, nestjs còn nhiều cách kết nối khác. Hôm nay chúng ta cùng nhau tìm hiểu về RabbitMQ.

Giới thiệu về RabbitMQ

RabbitMQ là một AMQP(Advanced Message Queuing Protocol ) message broker hay còn gọi là phần mềm quản lý hàng đợi message. Nói đơn giản, đây là phần mềm định nghĩa hàng đợi một ứng dụng khác có thể kết nối đến để bỏ message vào và gửi message dựa trên nó.

Một trong những khái niệm quan trọng cần phải biết đó là producer hay còn gọi là publisher, công việc của nó là gửi messages. Điều quan trọng thứ 2 là consumer, đợi để nhận message.

Giữa producer và consumer là queue. Nếu các bạn làm việc với queue ở những ngôn ngữ khác thì không thì không lạ với những khái niệm này. Nếu không thì chờ đón ở những bài viết sắp tới mình sẽ nói riêng về thằng queue này trong nestjs.

Chúng ta có thể liên kết trao đổi với nhiều hàng đợi khác nhau. Khi gửi message, producer có thể gửi message đến các hàng đợi được chỉ định.

Cuối cùng, consumer lấy message từ hàng đợi và xử lý nó.

Tóm tắt những điều trên:

  1. Producer gửi một message đến exchange,
  2. Exchange nhận được Message và chuyển nó đến hàng đợi mong muốn,
  3. Consumer lấy Message từ hàng đợi và xử lý nó.

Ưu điểm của việc sử dụng RabbitMQ với microservice

Với hàng đợi message, chúng ta có thể gửi message từ service này sang service khác mà không cần biết nó có xử lý được hay không. Do đó, chúng ta có thể tách biệt các microservice của mình nhiều hơn và làm cho chúng ít phụ thuộc vào nhau hơn. CloudAMQP, trong bài viết của mình , cũng đề cập rằng nó đã giúp họ rất nhiều trong việc duy trì kiến ​​trúc linh hoạt và sửa lỗi. Khi tìm thấy một lỗi, một vi dịch vụ bị lỗi có thể bị dừng và khởi động lại sau khi nó được giải quyết. Hàng đợi các tin nhắn có thể chồng chất khá nhiều, nhưng không có dữ liệu nào bị mất.

Chạy RabbitMQ với Docker Compose

Để cài đặt RabbitMQ chúng ta dùng docker cho nó thuận tiện nha. Phần cài đặt docker các bạn tự túc nhang.

Tạo file bên trong project chính. Thật ra tạo ở đâu cũng được, mà tạo ở đây dễ quản lý và thuận tiện khi ta setup trên server.

docker-composer.yml

version: "3"
services:
  rabbitmq:
    image: rabbitmq:3-management
    container_name: rabbitmq
    hostname: rabbitmq
    volumes:
      - /var/lib/rabbitmq
    ports:
      - "5672:5672"
      - "15672:15672"
    env_file:
      - ./rabbitmq.env

Trong này có phần config cho thằng rabbitmq. Ta cần tạo file này để khi cài đặt nó đọc dữ liệu ra

rabbitmq.env

RABBITMQ_DEFAULT_USER=admin
RABBITMQ_DEFAULT_PASS=admin

Sau đó chạy cmd để install rabbit về docker.

docker compose up
  • 5672 là cổng RabbitMQ chính mà ứng dụng NestJS của chúng ta sẽ sử dụng
  • 15672 là cổng của plugin quản lý. Nó cung cấp một giao diện cho phép chúng ta quản lý và giám sát phiên bản RabbitMQ của mình

Sử dụng RabbitMQ với NestJS

Việc sử dụng RabbitMQ với các microservice trong NestJS rất đơn giản, nhờ transporter được tích hợp trong NestJS. Tuy nhiên, chúng tôi cần cài đặt một số package bổ sung ở cả 2 project.

npm install amqplib amqp-connection-manager

Đồng thời cập nhật lại file môi trường .env ở cả 2 project:

RABBITMQ_USER=admin
RABBITMQ_PASSWORD=admin
RABBITMQ_HOST=localhost:5672
RABBITMQ_QUEUE_NAME=email-subscribers

Bên project chính của chúng ta, ca cần khai báo lại SUBSCRIBER_SERVICE bên trong SubscriberModule

import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { SubscriberController } from './controllers/subscriber.controller';
import { ClientProxyFactory, Transport } from '@nestjs/microservices';

@Module({
  imports: [ConfigModule],
  controllers: [SubscriberController],
  providers: [
    {
      provide: 'SUBSCRIBER_SERVICE',
      useFactory: (configService: ConfigService) => {
        // ClientProxyFactory.create({
        //   transport: Transport.TCP,
        //   options: {
        //     host: configService.get('SUBSCRIBER_SERVICE_HOST'),
        //     port: configService.get('SUBSCRIBER_SERVICE_PORT'),
        //   },
        // }),
        const user = configService.get('RABBITMQ_USER');
        const password = configService.get('RABBITMQ_PASSWORD');
        const host = configService.get('RABBITMQ_HOST');
        const queueName = configService.get('RABBITMQ_QUEUE_NAME');

        return ClientProxyFactory.create({
          transport: Transport.RMQ,
          options: {
            urls: [`amqp://${user}:${password}@${host}`],
            queue: queueName,
            queueOptions: {
              durable: true,
            },
          },
        });
      },
      inject: [ConfigService],
    },
  ],
})
export class SubscriberModule {}

Và ta đăng ký 1 router trong controller để call api

  @Post('rmq')
  @UseGuards(AuthGuard('jwt'))
  async createPost(@Req() req: any) {
    return this.subscriberService.send(
      {
        cmd: 'add-subscriber',
      },
      req.user,
    );
  }

Tới đây là xong phần code bên project chính.

Ta trở lại Microservice, thay đổi nhẹ phần kết nối tới RMQ trong file main.ts:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ConfigService } from '@nestjs/config';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const configService = app.get(ConfigService);
  // await app.connectMicroservice<MicroserviceOptions>({
  //   transport: Transport.TCP,
  //   options: {
  //     port: configService.get('PORT'),
  //   },
  // });

  const user = configService.get('RABBITMQ_USER');
  const password = configService.get('RABBITMQ_PASSWORD');
  const host = configService.get('RABBITMQ_HOST');
  const queueName = configService.get('RABBITMQ_QUEUE_NAME');

  await app.connectMicroservice<MicroserviceOptions>({
    transport: Transport.RMQ,
    options: {
      urls: [`amqp://${user}:${password}@${host}`],
      queue: queueName,
      queueOptions: {
        durable: true,
      },
    },
  });

  // await app.listen(3000);
  await app.startAllMicroservices();
}
bootstrap();

Bây giờ ta mở postman lên và test đi nào.

Kết luận

Trong bài viết này, chúng ta đã triển khai một microservice bằng RabbitMQ. Chúng ta đã xem qua tất cả những kiến ​​thức cơ bản về thiết lập giao tiếp với RabbitMQ và thảo luận về những ưu điểm của cách tiếp cận như vậy. Chúng ta cũng đã đi sâu hơn một chút và xem xét nội bộ của NestJS để hiểu cách nó sử dụng RabbitMQ bên trong tốt hơn một chút. Điều này chắc chắn đã cung cấp cho chúng ta khá nhiều thông tin tổng quan và lý do tại sao sử dụng RabbitMQ và cách sử dụng.

Rất mong được sự ủng hộ của mọi người để mình có động lực ra những bài viết tiếp theo.
{\__/}
( ~.~ )
/ > ♥️ I LOVE YOU 3000

JUST DO IT!