كيفية إعداد قوائم انتظار الرسائل للمهام غير المتزامنة مع RabbitMQ في تطبيقات Nest.js

إعداد قوائم انتظار الرسائل للمهام غير المتزامنة مع RabbitMQ في تطبيقات Nest.js
إعداد قوائم انتظار الرسائل للمهام غير المتزامنة مع RabbitMQ في تطبيقات Nest.js

مرحبا بمتابعي وزوار مدونة الشهادة مع مقال جديد بعنوان كيفية إعداد قوائم انتظار الرسائل للمهام غير المتزامنة مع RabbitMQ في تطبيقات Nest.js

عندما تقوم بتطوير البرامج، قد تؤدي بعض الخدمات إلى حظر أو إبطاء سرعة التطبيق الخاص بك. على سبيل المثال، المهام التي تتطلب استخدامًا مكثفًا لوحدة المعالجة المركزية (CPU) مثل نسخ الصوت أو معالجة الملفات.

لذا قد تتساءل – كيف تتأكد من تشغيل تطبيقك دون أن يتعطل؟ للتعامل مع هذا، يمكنك إرسال المهام إلى قائمة انتظار خارج تدفق التطبيق الخاص بك.

ما هي قائمة انتظار الرسائل؟

قائمة انتظار الرسائل هي أداة تسهل الاتصال ونقل البيانات بين الخدمات داخل تطبيق واحد (أو خارجيًا). يقوم بتخزين هذه البيانات أو الرسائل باستخدام مبدأ First-In-First-Out (FIFO). وهذا يعني أن البيانات القديمة التي تم تمريرها إلى قوائم الانتظار هذه تتم معالجتها قبل البيانات الأحدث.

تشكل المكونات المختلفة قائمة انتظار الرسائل، مثل:

  • الرسائل : هذه هي البيانات التي يتم إرسالها إلى قائمة الانتظار. وغالبا ما يشار إليها بالوظائف.
  • قوائم الانتظار : هذه هي هياكل البيانات المستخدمة لتخزين الرسائل.
  • المنتجون : هذه هي الخدمة التي ترسل الرسائل أو البيانات إلى نظام قائمة الانتظار.
  • المستهلكون : هذه هي الخدمة التي تستمع إلى قائمة الانتظار وتنفذ الرسائل التي تم تمريرها فيها.

أدوات وضع الرسائل في قائمة انتظار

الآن، هناك العديد من أدوات قائمة انتظار الرسائل التي يمكنك استخدامها في الأنظمة غير المتزامنة، مثل ما يلي:

  • RabbitMQ : خيار موثوق ومرن لتنفيذ قوائم انتظار الرسائل في التطبيقات.
  • Apache Kafka : أداة فعالة لترتيب الرسائل، كما أنها جيدة جدًا في معالجة تدفق الأحداث.
  • Redis : مخزن في الذاكرة يستخدم لقائمة انتظار الرسائل والتخزين المؤقت ومعالجة البيانات.

لاحظ أن بعض هذه الأدوات لا تقتصر على قائمة انتظار الرسائل ولكن يمكن استخدامها لأغراض أخرى أيضًا، مثل معالجة الدفق.

في هذه المقالة، ستقوم بإنشاء مشروع Nest.js بسيط والذي سيستخدم RabbitMQ كموفر خدمة قائمة انتظار الرسائل.

سيتم تقسيم البرنامج التعليمي إلى 3 أجزاء:

  • كيفية إعداد مشروع Nest.js لتدفق تسجيل المستخدم الأساسي
  • كيفية إعداد خدمة البريد الإلكتروني لتسجيل المستخدم
  • كيفية دمج خدمة قائمة الانتظار باستخدام RabbitMQ

المتطلبات الأساسية

  • ستحتاج إلى تثبيت Node على نظامك. إذا لم يكن لديك، فإليك موقعه الرسمي: https://nodejs.org/en .
  • ستحتاج إلى تثبيت Node Package Manager (NPM)، والذي يمكنك تنزيله هنا إذا لم يكن لديك: https://docs.npmjs.com/downloading-and-installing-node-js-and-npm .
  • ستحتاج إلى تثبيت RabbitMQ. إليك المكان الذي يمكنك الحصول عليه في حالة عدم حصولك عليه بعد: https://www.rabbitmq.com/download.html
  • ستحتاج إلى أن يكون لديك محرر نصوص. في هذه المقالة، سأستخدم VSCode. يمكنك تنزيله هنا: https://code.visualstudio.com/download أو استخدم محرر الأكواد الذي تختاره.

كيفية إعداد مشروع Nest.js

يعد تشغيل تطبيق Nest.js أمرًا سريعًا وبسيطًا إذا كنت تستخدم Nest CLI. افتح المحطة الطرفية الخاصة بك وأدخل هذا الأمر أدناه لتثبيت واجهة سطر الأوامر:

 $ npm install -g @nestjs/cli

يؤدي هذا إلى تثبيت Nest.js CLI عالميًا على نظامك، مما يعني أنه يمكنك استدعاء أوامر CLI بغض النظر عن الدليل الذي تتواجد فيه حاليًا.

من الآن فصاعدا، لإنشاء مشروع REST API بسيط، سوف تقوم بإدخال الأمر أدناه:

nest new simple-queue

قائمة الانتظار البسيطة هنا هي اسم الدليل الذي سيتم إنشاؤه. يمنحك إدخال هذا الأمر مطالبة بتحديد مدير الحزم.

عند الانتهاء من ذلك، انتقل إلى الدليل الذي تم إنشاؤه وافتحه في محرر النصوص الخاص بك عن طريق إدخال هذا الأمر:

cd simple-queue && code .

يؤدي هذا إلى فتح محرر النصوص الخاص بك. نريد العمل على مشروع يُظهر بشكل أفضل كيف يمكن استخدام قائمة انتظار الرسائل في سيناريو حقيقي – لذلك دعونا نقوم بإعداد نموذج تسجيل مستخدم أساسي. عند إدخال البيانات بنجاح، يتم إرسال بريد إلكتروني إلى المستخدم، ولكنك ستتعامل مع خدمة البريد الإلكتروني بشكل منفصل عن طريق تمريرها إلى قائمة انتظار لتحسين الأداء.

لهذا، سنستخدم قاعدة بيانات SQLite وTypeOrm ومدققي الفئة وحزمة dotenv حتى تتمكن من تأمين متغيرات التكوين الخاصة بك. تابع تثبيتها عن طريق كتابة هذا الأمر في جهازك:

npm install --save @nestjs/typeorm typeorm sqlite3 class-validator dotenv

عند اكتمال التثبيت، انتقل إلى وحدة التطبيق الجذر، ثم قم بتضمين تكوين TypeOrm لقاعدة البيانات الخاصة بك.

SQLite هي قاعدة بيانات SQL خفيفة الوزن تسمح لنا بتدوير البيانات واختبارها بسرعة. إنه الأمثل لحالة الاستخدام هذه – والآن سنقوم بتكوينه.

تكوين قاعدة بيانات SQLite

import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type:'sqlite',
      database: 'mini-db.sqlite',
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
      synchronize: true,  
  })],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

تهاني! لقد نجحت في توصيل قاعدة بيانات بمشروعك. حان الوقت الآن لإنشاء الخدمات التي ستتعامل مع تسجيل المستخدم.

للقيام بذلك، سيتعين عليك العودة إلى صديقك العزيز Nest CLI. هناك، ستُدخل أمرًا مختلفًا للمساعدة في إنشاء مجلد موارد للمستخدم، والذي سيحتوي على الكيان والخدمة وdto ووحدة التحكم.

للقيام بذلك، افتح المحطة الطرفية الخاصة بك وأدخل هذا الأمر:

nest generate resource users

سيتم عرض مطالبة بتحديد طبقة النقل الخاصة بك. حدد الأول وهو REST API. بعد ذلك، ستسألك مطالبة أخرى عما إذا كنت ترغب في إنشاء نقاط نهاية CRUD – يمكنك كتابة نعم. ومن ثم يمكنك إجراء التعديلات وفقًا لمتطلباتك.

للمتابعة، عليك أولاً تحديد المعلومات التي يجب أن تكون لدى كل مستخدم. أولاً، قم بإنشاء كيان المستخدم. يمكنك القيام بذلك عن طريق الانتقال إلى ملف كيان المستخدم في المجلد الفرعي للكيان الذي تم إنشاؤه في مجلد المستخدم. ثم حدد بيانات المستخدم مثل هذا:

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";

@Entity('users')
export class User {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column({ length: 100, unique: true })
  username: string;

  @Column({ length: 100, unique: true })
  email: string;
}

بالنسبة لهذا المشروع الصغير، ستستخدم بيانات المستخدم الأساسية لتسريع العملية. تم تعيين اسم المستخدم وحقل البريد الإلكتروني ليكونا فريدين، مما يعني أنه لن يكون هناك تكرار لمثيل البيانات الذي تم تمريره لجدول المستخدم هذا.

الآن وبعد القيام بذلك، قم بتعديل ملف إنشاء المستخدم dto الذي تم إنشاؤه على النحو التالي:

import { IsNotEmpty, IsString, IsEmail } from "class-validator";

export class CreateUserDto {
    @IsNotEmpty()
    @IsString()
    username: string;
  
    @IsNotEmpty()
    @IsString()
    @IsEmail()
    email: string;
  }

تم إنشاء هذا للتحقق من صحة الحمولة التي سيتم إرسالها في طلبك باستخدام حزمة مدقق الفئة.

الآن، قم بتعديل createالطريقة في ملف خدمة المستخدم.

import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { CreateUserDto } from "./dto/create-user.dto";
import { User } from "./entities/user.entity";

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>
  ) {}
  async create(createUserDto: CreateUserDto): Promise<User> {
    const newUser = this.userRepository.create(createUserDto);
    return await this.userRepository.save(newUser);
  }
}

بعد ذلك ستقوم بتعديل ملف وحدة التحكم. لقد قمت بالفعل بتعريف createنقطة النهاية، لذا سيتعين عليك فقط تنظيف نقاط النهاية الأخرى غير المطلوبة.

import { Controller, Post, Body } from "@nestjs/common";
import { CreateUserDto } from "./dto/create-user.dto";
import { UsersService } from "./users.service";

@Controller('users')
export class UsersController {
 constructor(private readonly usersService: UsersService) {}
  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }
}

افتح ملف وحدة المستخدم وقم بإجراء بعض التعديلات عن طريق إضافة حقل الاستيراد إلى مصمم الوحدة واستخدام خاصية TypeOrmModule.

import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}

بعد ذلك، قم بتشغيل الخادم الخاص بك عن طريق إدخال هذا الأمر على جهازك الطرفي: npm run start:dev. بمجرد تشغيل الخادم، افتح عميل API الذي تختاره. في هذه المقالة، سوف نستخدم ساعي البريد. ثم قم بإجراء طلب POST إلى نقطة النهاية، والتي ستكون localhost:3000/usersتوفير بيانات الحمولة المطلوبة.

تم تقديم طلب وتم إنشاء مثيل مستخدم.

الخطوة التالية هي إضافة خدمة بريد إلكتروني إلى مشروعك والتي ستساعد في إخطار المستخدمين الجدد الذين يقومون بالتسجيل.

كيفية إعداد خدمة البريد الإلكتروني لتسجيل المستخدم

ولهذا الغرض، ستستخدم بعض الحزم المطلوبة لإنشاء خدمة بريد إلكتروني. افتح جهازك الطرفي وأدخل الأمر أدناه لتثبيت هذه الحزم:

npm install --save @nestjs-modules/mailer nodemailer

عند تثبيت هذه الحزم، يمكنك الآن تنفيذ خدمة البريد. باستخدام Nest CLI، قم بإنشاء وحدة إرسال وخدمة عن طريق إدخال هذا الأمر في الجهاز الطرفي الخاص بك:

nest generate module email && nest generate service email

عند الانتهاء، افتح ملف الوحدة الذي تم إنشاؤه حديثًا في مجلد البريد. ستستخدم خاصية MailerModule الخاصة بالحزمة @nestjs-modules/mailerلتكوين خدمة البريد الخاصة بك هنا. يتطلب الأمر عميل SMTP الذي ستحتاج إلى مفاتيحه لتكوين MailerModule هذا.

لذلك يمكنك استخدام https://app.elasticemail.com للحصول على مفاتيح SMTP هذه. قم بالتسجيل والاتصال بـ SMTP API. ستحصل بعد ذلك على مفاتيح لاستخدامك الخاص.

لاحظ أن هذا الوضع المجاني لعميل SMTP له قيود ولا يمكنه الإرسال إلى جميع رسائل البريد الإلكتروني – لذا يجب عليك استخدام خدمة بريد إلكتروني تجريبية.

كيفية تكوين وحدة الإرسال

بمجرد الانتهاء من هذا الإعداد، ارجع إلى التطبيق الخاص بك وقم بإنشاء ملف .env . قم بتعيين أسرارك لمفاتيح SMTP. ثم قم بتكوين MailerModule الخاص بك مثل هذا:

import { Global, Module } from "@nestjs/common";
import { EmailService } from "./email.service";
import { MailerModule } from "@nestjs-modules/mailer";

require('dotenv').config();
@Global()
@Module({
  imports: [
    MailerModule.forRoot({
      transport: {
        service: 'QueueTest',
        host: process.env.SMTP_HOST,
        port: process.env.SMTP_PORT,
        auth: {
          user: process.env.SMTP_USER,
          pass: process.env.SMTP_PASSWORD,
        },
      },
      defaults: {
        from: process.env.FROM_EMAIL,
      },
    }),
  ],
  providers: [EmailService]
})
export class EmailModule {}

تم ضبط الديكور العام للتأكد من إمكانية استدعاء MailModule في أي مكان في تطبيقك. تأكد من تحميل أسرارك بشكل صحيح وأن لديك بريدًا إلكترونيًا صالحًا تم تعيينه من:process.env.FROM_EMAIL.

تحقق للتأكد من استيراد EmailModule أيضًا في وحدة التطبيق الجذر بنفس الطريقة التي تم بها استيراد UsersModule الخاص بك في مصفوفة الاستيراد الخاصة بوحدة التطبيق.

بعد ذلك، افتح ملف خدمة البريد الإلكتروني الخاص بك – ستحتاج إلى إجراء بعض التعديلات على فئة EmailService. قم بإضافة مُنشئ واستدعاء خاصية MailService من @nestjs-modules/mailerالحزمة. ثم تابع وأنشئ وظيفة تتولى إرسال رسائل البريد الإلكتروني.

يوجد أدناه فئة وطريقة تقوم بذلك:

import { MailerService } from '@nestjs-modules/mailer';
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';

@Injectable()
export class EmailService {
  constructor(private mailerService: MailerService) {}
  async sendEmail(options: { email: string; subject: string; html: string;
  }) {
    try {
      const message = {
        to: options.email,
        subject: options.subject,
        html: options.html
      };
      const emailSend = await this.mailerService.sendMail({
        ...message,
      });
      return emailSend;
    } catch (error) {
      throw new HttpException('Error', HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }
}

لقد حددت الآن طريقة إرسال البريد الإلكتروني. لقد قمت أيضًا بوضع معالج استثناء لمعالجة الأخطاء بشكل أفضل.

حان الوقت الآن لإضافة هذه الخدمة التي تم إنشاؤها حديثًا إلى تدفق تسجيل المستخدم الخاص بك.

انتقل إلى ملف خدمة المستخدم الخاص بك، وأضف خدمة البريد إلى مُنشئك كموفر. ثم اتصل بالخدمة في create userطريقتك مثل هذا:

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
    private emailService: EmailService
  ) {}
  async create(createUserDto: CreateUserDto): Promise<User> {
    const newUser = this.userRepository.create(createUserDto);
    const user =  await this.userRepository.save(newUser);
      const emailData = {
        email: user.email,
        subject: 'Welcome to Our Community',
        html: `<p>Hello ${user.username},</p>
        <p>Welcome to our community! Your account is now active.</p>
        <p>Enjoy your time with us!</p>`,
      };
      await this.emailService.sendEmail(emailData)
    return user
  }
}

تأكد من تعديل الوحدات النمطية الخاصة بك لتصحيح أي أخطاء في إدخال التبعية. في ملف وحدة البريد الإلكتروني الخاص بك، قم بإضافة EmailService إلى مصفوفة التصدير:

 providers: [EmailService],
 exports: [EmailService]

أضفه أسفل موفري الخدمات لديك لتصدير خدمة البريد الإلكتروني بحيث يمكن الوصول إليها في وحدات أخرى. ثم قم باستيراد EmailModule إلى ملف وحدة المستخدم الخاص بك وأضفه إلى مصفوفة الاستيراد الخاصة بك مثل هذا:

@Module({
  imports: [TypeOrmModule.forFeature([User]), EmailModule],
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}

الآن حان الوقت لاختباره. احصل على حساب مجاني من أي منصة لاختبار البريد الإلكتروني عبر الإنترنت وافتح Postman. قم بتقديم طلب إلى create userنقطة النهاية باستخدام بريدك الإلكتروني الصحيح. يجب أن تحصل على رد عبر البريد الإلكتروني مثل هذا:

رد البريد الإلكتروني الذي يجب أن تحصل عليه

كيفية دمج خدمة قائمة الانتظار باستخدام RabbitMQ

للبدء في هذا، سيتعين عليك تثبيت بعض الحزم التي تتيح لك تنفيذ قوائم الانتظار باستخدام RabbitMQ. أدخل الأمر أدناه لتثبيت هذه الحزم:

npm install --save amqplib @types/amqplib amqp-connection-manager

تكوين خدمة المنتج

بمجرد اكتمال التثبيت، حان الوقت لتكوين RabbitMQ. ستقوم بإنشاء مجلد جديد في دليل src الخاص بك وتسميته قوائم الانتظار. ثم قم بإنشاء ملف منتج قائمة الانتظار. قم باستيراد هذه الحزم وقم بإعدادها على النحو التالي:

import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
import amqp, { ChannelWrapper } from 'amqp-connection-manager';
import { Channel } from 'amqplib';

@Injectable()
export class ProducerService {
  private channelWrapper: ChannelWrapper;
  constructor() {
    const connection = amqp.connect(['amqp://localhost']);
    this.channelWrapper = connection.createChannel({
      setup: (channel: Channel) => {
        return channel.assertQueue('emailQueue', { durable: true });
      },
    });
  }

  async addToEmailQueue(mail: any) {
    try {
      await this.channelWrapper.sendToQueue(
        'emailQueue',
        Buffer.from(JSON.stringify(mail)),
        {
          persistent: true,
        },
      );
      Logger.log('Sent To Queue');
    } catch (error) {
      throw new HttpException(
        'Error adding mail to queue',
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }
}

تم تعيين اتصال AMQP وهو قيد التشغيل على مضيف محلي باستخدام منفذ RabittMQ الافتراضي وهو 5432. وقمت أيضًا بإنشاء قناة على هذا الاتصال بإدخال خيار يتم تنفيذه في أي وقت يتم فيه إنشاء قناة جديدة. يساعد هذا إذا كان لديك أي تكوين لهذه القناة.

لقد قمت أيضًا بإنشاء emailQueueخاصية assurcerQueue والتي تتحقق من عدم وجود قائمة انتظار بهذا الاسم بالفعل. إذا كان موجودا، فإنه ليس له أي تأثير، لذلك فهو عاجز.

ثم قمت بإنشاء خيار durable: trueللتأكد من أن قائمة الانتظار ستصمد أمام إعادة تشغيل الخادم.

بعد ذلك، قمت بتحديد طريقة إضافة بيانات البريد الإلكتروني إلى قائمة الانتظار. يؤدي هذا إلى استدعاء sendToQueueخاصية ChannelWrapper، وتمرير اسم قائمة الانتظار الذي تريد إرسال البيانات إليه. من الناحية المثالية، يجب أن يكون بنفس الاسم الذي قمت بتعريفه باستخدام خاصية AcceptQueue.

الوسيطة الثانية هي بيانات البريد، ولكن أولاً قمت بتحويلها إلى سلسلة JSON ثم إلى مخزن مؤقت. يمكنك القيام بذلك لأن الرسائل الموجودة في RabbitMQ يتم إرسالها في الغالب كبيانات ثنائية.

يمكنك بعد ذلك تعيين خيار persistent: trueللتأكد من عدم فقدان البيانات المرسلة إلى قائمة الانتظار في حالة تعطل الخادم. وبعد ذلك، مع بعض معالجة الأخطاء وطريقة إرسال الرسائل إلى قائمة الانتظار، يكون الأمر جيدًا.

إعداد خدمة المستهلك

الآن بعد أن قمت بتكوين خدمة المنتج، حان الوقت لإعداد خدمة المستهلك.

قم بإنشاء ملف آخر في المجلد الفرعي لقائمة الانتظار. إنه مشابه تمامًا، لكن في هذه الحالة، ستستهلك البيانات من قائمة الانتظار. فيما يلي التكوين لخدمة المستهلك:

import { Injectable, OnModuleInit, Logger } from '@nestjs/common';
import amqp, { ChannelWrapper } from 'amqp-connection-manager';
import { ConfirmChannel } from 'amqplib';
import { EmailService } from 'src/email/email.service';

@Injectable()
export class ConsumerService implements OnModuleInit {
  private channelWrapper: ChannelWrapper;
  private readonly logger = new Logger(ConsumerService.name);
  constructor(private emailService: EmailService) {
    const connection = amqp.connect(['amqp://localhost']);
    this.channelWrapper = connection.createChannel();
  }

  public async onModuleInit() {
    try {
      await this.channelWrapper.addSetup(async (channel: ConfirmChannel) => {
        await channel.assertQueue('emailQueue', { durable: true });
        await channel.consume('emailQueue', async (message) => {
          if (message) {
            const content = JSON.parse(message.content.toString());
            this.logger.log('Received message:', content);
            await this.emailService.sendEmail(content);
            channel.ack(message);
          }
        });
      });
      this.logger.log('Consumer service started and listening for messages.');
    } catch (err) {
      this.logger.error('Error starting the consumer:', err);
    }
  }
}

أولاً، قمت بتحديد فئة المستهلك الخاصة بك. ولهذا الغرض، فإنه يطبق onModuleInitالواجهة التي يوفرها @nestJs/common. يحدد هذا أن الفئة المحددة يجب أن يكون لها طريقة تسمى onModuleInit().

كما يقول الاسم، سيتم استدعاء الطريقة تلقائيًا أثناء تهيئة الوحدة، وذلك عندما يتم تحميل الوحدة التي تحتوي على هذه الفئة.

في منشئ الفصل، قمت بإضافة لأنك emailServiceستستخدم sendEmailطريقة هذا الفصل.

في onModuleInit()الطريقة، قمت بتحديد قناة. يعد هذا ضروريًا لأنك تحتاج إلى قناة لاستهلاك الرسائل من قائمة الانتظار.

ومن هذا المنطلق، يتم استخدام القناة بعد ذلك لتأكيد قائمة الانتظار التي يجب أن تكون مشابهة في الاسم والخيارات لما لديك في خدمة المنتج الخاصة بك. إذا لم يكن الأمر كذلك، فلن تتمكن من الاستماع إلى قائمة الانتظار التي تم إنشاؤها على خدمة المنتج.

ثم استخدمت طريقة الاستهلاك للقناة للاستماع إلى الرسالة الواردة من قائمة الانتظار التي قمت بتسجيلها وتنفيذها.

تذكر أنه من قبل، كان عليك تحويل الرسالة إلى Buffer لإرسالها إلى قائمة الانتظار. الآن عليك تحويله إلى كائن JavaScript. ثم اتصل بطريقة emailService لإرسال بريد إلكتروني وتمرير كائن JavaScript المحول كوسيطة لهذه الطريقة.

أخيرًا، قمت باستدعاء ackالطريقة المستخدمة لإعلام قائمة الانتظار بأن الرسالة قد تم استلامها ومعالجتها بنجاح حتى تتم إزالتها من قائمة الانتظار.

الآن بعد أن قمت بتعريف هذه الخدمات، قم بإنشاء ملف وحدة نمطية وقم بتعيينها في مصفوفة الموفرين. ثم قم بتصدير خدمة المنتج لأنك ستستدعيها في وحدة نمطية أخرى.

import { Module } from '@nestjs/common';
import { ConsumerService } from './consumer.file';
import { ProducerService } from './producer.file';

@Module({
  providers: [ProducerService, ConsumerService],
  exports: [ProducerService],
})
export class QueueModule {}

الخطوة التالية هي إضافة رسائل البريد الإلكتروني التي يتم إرسالها عند تسجيل المستخدم إلى خدمة قائمة الانتظار التي أنشأتها للتو.

انتقل مرة أخرى إلى ملف خدمة المستخدم الخاص بك وقم بإجراء بعض التعديلات: استبدل خدمة البريد الإلكتروني بخدمة المنتج كموفر في المنشئ، ثم اتصل بالخدمة وطريقة الإضافة إلى قائمة انتظار البريد الإلكتروني كما هو موضح أدناه:

import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { CreateUserDto } from "./dto/create-user.dto";
import { User } from "./entities/user.entity";
import { ProducerService } from "src/queues/producer.file";

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
    private producerService: ProducerService,
  ) {}
  async create(createUserDto: CreateUserDto): Promise<User> {
    const newUser = this.userRepository.create(createUserDto);
    const user = await this.userRepository.save(newUser);
    const emailData = {
      email: user.email,
      subject: 'Welcome to Our Community',
      html: `<p>Hello ${user.username},</p>
        <p>Welcome to our community! Your account is now active.</p>
        <p>Enjoy your time with us!</p>`,
    };
    await this.producerService.addToEmailQueue(emailData);
    return user;
  }
}

أيضًا في ملف وحدة المستخدم، استبدل EmailModule بوحدة QueueModule لتجنب أخطاء إدخال التبعية عند بدء تشغيل الخادم الخاص بك.

import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { QueueModule } from 'src/queues/queue.module';

@Module({
  imports: [TypeOrmModule.forFeature([User]), QueueModule],
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}

والآن أخيرًا، حان الوقت لاختبار تدفق تسجيل المستخدم مرة أخرى. لذا انتقل مرة أخرى إلى Postman ثم اكتب بريدًا إلكترونيًا صالحًا واسم مستخدم واضغط على Enter. على المحطة الطرفية لخادمك قيد التشغيل، سترى السجلات التي تم تعيينها لتتبع الطريقة التي تم بها إرسال الرسالة وكيفية استلامها وتنفيذها.

السجلات التي تساعدك على تتبع الرسالة

يمكنك أيضًا فتح لوحة معلومات RabbitMQ لعرض نشاط قائمة الانتظار على http://localhost:15672 ، بشكل افتراضي يكون المستخدم “ضيفًا”، لذا أدخل guestاسم المستخدم وكلمة المرور.

قوائم الانتظار والتدفقات في RabbitMQ

إليك الرابط إلى مستودع GitHub . لا تتردد في التحقق من ذلك كلما كنت عالقا.

خاتمة

في هذه المقالة، تعرفت على ماهية قائمة انتظار الرسائل بالإضافة إلى بعض المكونات الرئيسية لكيفية عملها. لقد قمت أيضًا ببناء مشروع Nest.js صغير وقمت بتنفيذ خدمة البريد الإلكتروني فيه. وأخيرًا، قمت بدمج خدمة قائمة الانتظار في مشروعك، موضحًا كيفية عملها في سيناريو واقعي.

يعد فهم سلوكيات وأنماط قائمة انتظار الرسائل مهارة أساسية عند تطوير تطبيقات قابلة للتطوير. ويساعد ذلك على تقليل التأخر وتحسين سرعة وكفاءة تطبيقاتك.

أتمنى أنك استمتعت بقراءة هذا المقال. يمكنك متابعة لي على فيس بوك و تويتر .

نسعى دائما لتقديم كل ماهو ممتع لكم فاتمنى ان نكون عند حسن ظنكم جميعا.

فارجو دعمنا وتشجيعنا على تقديم الافضل بمشاركة الموضوع مع اصدقائكم على مواقع التواصل الاجتماعى فيس بوك و تويتر عن طريق ازرار المشاركة اسفل التدوينة.

وترك تعليق داخل صندوق التعليقات تشجيعاُ لنا كل الود والاحترام لكم والى اللقاء فى تدوينة اخرى من تدوينات مدونة الشهادة .