اگر بتوانید آن را به بعد موکول کنید، چرا فوراً کاری را انجام دهید؟ صف پیام برای افراد تنبل نیست – می تواند به پاسخگویی بیشتر برنامه شما کمک کند.
به عنوان یک توسعهدهنده وب، بسیار مهم است که برنامههای شما تا جایی که میتوانند سریع عمل کنند. شما باید برنامه های وب بسازید که به درخواست ها در سریع ترین زمان ممکن پاسخ دهند.
یکی از فناوریهایی که میتواند به شما کمک کند، صفبندی وظایف است.
بنابراین، صف بندی وظیفه چیست و چگونه می توانید از آن برای بهینه سازی یک برنامه Node.js استفاده کنید؟
Task Quueing چیست؟
صف پیام وسیله ای برای ارتباط ناهمزمان بین دو برنامه یا سرویس است که معمولاً تولید کننده و مصرف کننده نامیده می شود. این یک مفهوم شناخته شده است که در معماری های بدون سرور و میکروسرویس به کار می رود.
مفهوم وظیفه یا صف شغل از صف پیام برای بهبود عملکرد برنامه استفاده می کند. پیچیدگیهای مدیریت پیامها را انتزاعی میکند و شما را قادر میسازد تا عملکردهایی را برای مدیریت کارها یا وظایف به صورت ناهمزمان با استفاده از یک صف تعریف کنید، در نتیجه میزان استفاده از حافظه را در برخی از بخشهای یک برنامه کاهش میدهد.
رایج ترین نمونه نرم افزار صف پیام RabbitMQ است. ابزارهای Task queue عبارتند از Celery و Bull. شما همچنین می توانید RabbitMQ را به گونه ای پیکربندی کنید که به عنوان یک صف کار کار کند. برای آشنایی با صف بندی وظایف در Node.js با استفاده از Bull به ادامه مطلب مراجعه کنید.
BullMQ چیست؟
BullMQ (Bull.js) یک کتابخانه Node.js است که برای پیاده سازی صف در برنامه های Node استفاده می شود. Bull یک سیستم مبتنی بر Redis است (شاید بیشتر با Redis به عنوان ابزاری برای ذخیره سازی سریع داده ها آشنا باشید) و یک گزینه سریع و قابل اعتماد برای در نظر گرفتن صف وظایف در Node.js است.
شما می توانید از Bull برای بسیاری از وظایف مانند اجرای کارهای تاخیری، کارهای برنامه ریزی شده، کارهای تکرار شونده، صف های اولویت دار و بسیاری موارد دیگر استفاده کنید.
بنابراین، چگونه می توانید از Bull و Redis برای اجرای وظایف Node.js به صورت ناهمزمان استفاده کنید؟
نحوه پیکربندی Bull و Redis برای Task Queuing در Node.js
برای شروع کار در Node.js با Bull، باید Node.js و Redis را روی دستگاه خود نصب کنید. اگر Redis را نصب نکردهاید، میتوانید از راهنمای آزمایشگاههای Redis برای نصب آن پیروی کنید.
اولین قدم برای پیاده سازی Bull این است که آن را با اجرای npm install bull یا yarn add bull در ترمینال داخل پوشه پروژه خود به وابستگی های پروژه خود اضافه کنید. راه های مختلفی برای مقداردهی اولیه یک صف در Bull وجود دارد که در زیر نشان داده شده است:
const Queue = require('bull');
// different ways to initialize a queue
// - using redis URL string
const emailQueue = new Queue('Email Queue', 'redis://127.0.0.1:6379');
// - with a redis connection and queue options object
const videoQueue = new Queue('Video Queue', 'redis://127.0.0.1:6379', queueOptions);
// - without a redis connection but with queueOption
const docQueue = new Queue('Document Queue', queueOptions);
// - without a redis connection or queue options
const QueueClient = new Queue('My Queue');
همه اینها از حداقل پیکربندی برای Bull در Node.js استفاده می کنند. شی گزینه از ویژگی های بسیاری پشتیبانی می کند و می توانید در بخش گزینه های صف در مستندات Bull با آنها آشنا شوید.
پیاده سازی صف وظایف ایمیل با استفاده از BullMQ
برای اجرای یک صف برای ارسال ایمیل، میتوانید تابع تولیدکننده خود را که ایمیلها را به صف ایمیل اضافه میکند، و یک تابع مصرفکننده برای ارسال ایمیلها تعریف کنید.
ابتدا، میتوانید صف خود را در یک کلاس با استفاده از URL Redis و برخی از گزینههای صف که در زیر مشاهده میکنید، مقداردهی اولیه کنید.
// queueHandler.js
const Queue = require('bull');
// use a real email handler module here - this is just an example
const emailHandler = require('./emailHandler.js');
// define constants, Redis URL, and queue options
const REDIS_URL = 'redis://127.0.0.1:6379';
const queueOpts = {
// rate limiter options to avoid overloading the queue
limiter: {
// maximum number of tasks queue can take
max: 100,
// time to wait in milliseconds before accepting new jobs after
// reaching limit
duration: 10000
},
prefix: 'EMAIL-TASK', // a prefix to be added to all queue keys
defaultJobOptions: { // default options for tasks in the queue
attempts: 3, // default number of times to retry a task
// to remove a task from the queue after completion
removeOnComplete: true
}
};
class EmailQueue {
constructor() {
this.queue = new Queue('Email Queue', REDIS_URL, queueOpts);
}
};
export default EmailQueue; // export the class
اکنون که یک صف را مقداردهی اولیه کرده اید، می توانید تابع تولید کننده خود را (با استفاده از تابع ()add Bull) به عنوان متدی از کلاس EmailQueue برای افزودن ایمیل ها به صف وظایف تعریف کنید. بلوک کد زیر این را نشان می دهد:
// queueHandler.js
class EmailQueue {
constructor () {
// ...
}
// producer function to add emails to queue
async addEmailToQueue(emailData) {
// add task with name 'email_notification' to queue
await this.queue.add('email_notification', emailData);
console.log('the email has been added to the queue...');
}
};
export default EmailQueue; // export the class
تابع تولید کننده آماده است، و اکنون می توانید یک تابع مصرف کننده (با استفاده از تابع Bull’s process()) برای پردازش تمام وظایف ایمیل در صف تعریف کنید—یعنی. برای ارسال ایمیل با تابع تماس بگیرید. شما باید این تابع مصرف کننده را در سازنده کلاس تعریف کنید.
// queueHandler.js
class EmailQueue {
constructor () {
// ...
// consumer function that takes in the assigned name of the task and
// a callback function
this.queue.process('email_notification', async (emailJob, done) => {
console.log('processing email notification task');
await emailHandler.sendEmail(emailJob); // send the email
done(); // complete the task
})
}
// ...
};
export default EmailQueue; // export the class
یک کار همچنین ممکن است گزینه هایی برای تعریف رفتار خود در صف یا نحوه مدیریت عملکرد مصرف کننده داشته باشد. شما می توانید در مورد این موضوع بیشتر بدانید
بخش گزینه های شغلی از مستندات Bull
.
آرگومان emailJob یک شی است که حاوی ویژگی های وظیفه برای پردازش صف است. همچنین شامل داده های اصلی مورد نیاز برای ساخت ایمیل است. برای درک آسان، تابع sendEmail() مشابه این مثال خواهد بود:
// emailHandler.js
const sendgridMail = require('@sendgrid/mail');
const apiKey = process.env.SENDGRID_API_KEY
sendgridMail.setApiKey(apiKey); // set email transporter security credentials
const sendEmail = async (emailJob) => {
try {
// extract the email data from the job
const { name, email } = emailJob.data;
const message = {
from: 'me@example.com',
to: 'you@example.com',
subject: 'Hi! Welcome',
text: `Hello ${name}, welcome to MUO`
};
await sendgridMail.sendMail(message); // send email
// mark task as completed in the queue
await emailJob.moveToCompleted('done', true);
console.log('Email sent successfully...');
} catch (error) {
// move the task to failed jobs
await emailJob.moveToFailed({ message: 'task processing failed..' });
console.error(error); // log the error
}
}
export default sendEmail;
اکنون که توابع تولید کننده و مصرف کننده را تعریف کرده اید و آماده استفاده هستید، اکنون می توانید با تابع تولید کننده خود در هر نقطه از برنامه خود تماس بگیرید تا یک ایمیل به صف پردازش اضافه کنید.
یک کنترلر نمونه به شکل زیر است:
// userController.js
const EmailQueue = require('../handlers/queueHandler.js')
const signUp = async (req, res) => {
const { name, email, password } = req.body;
// --
// a query to add the new user to Database...
// --
// add to Email queue
const emailData = { name, email };
await EmailQueue.addEmailToQueue(emailData);
res.status(200).json({
message: "Sign up successful, kindly check your email"
})
}
فایل queueHandler.js شما اکنون باید به صورت زیر باشد:
// queueHandler.js
const Queue = require('bull');
const emailHandler = require('../handlers/emailHandler.js');
const REDIS_URL = 'redis://127.0.0.1:6379';
const queueOpts = {
limiter: {
max: 100,
duration: 10000
},
prefix: 'EMAIL-TASK',
defaultJobOptions: {
attempts: 3,
removeOnComplete: true
}
};
class EmailQueue {
constructor() {
this.queue = new Queue('Email Queue', REDIS_URL, queueOpts);
// consumer
this.queue.process('email_notification', async (emailJob, done) => {
console.log('processing email notification task');
await emailHandler.sendEmail(emailJob);
done();
})
}
// producer
async addEmailToQueue(emailData) {
// add task with name 'email_notification' to queue
await this.queue.add('email_notification', emailData);
console.log('the email has been added to the queue...');
}
};
export default EmailQueue;
هنگامی که این مورد را در Node.js REST API پیادهسازی میکنید، متوجه کاهش زمان پاسخدهی نقطه پایانی ثبتنام و زمان تحویل ایمیل سریعتر در مقایسه با جایگزین خواهید شد.
صف های وظیفه همچنین شما را قادر می سازد تا خطاهای ثبت نام و ایمیل را به طور مستقل مدیریت کنید.
بهینه سازی برنامه ها با استفاده از صف های وظیفه
صف های پیام و وظایف راه بسیار خوبی برای بهبود عملکرد کلی برنامه ها هستند. آنها همچنین بسیار ارزان هستند و شما می توانید در قسمت های مختلف یک برنامه که نیاز دارید از آنها استفاده کنید.
اگرچه این آموزش از ایمیل ها به عنوان یک سناریوی نمونه برای رسیدگی به کارهایی که حافظه مصرف می کنند با صف ها استفاده می کند، موارد زیادی وجود دارد که می توانید مفاهیم مشابهی را به کار ببرید. اینها شامل عملیات سنگین خواندن/نوشتن، ارائه تصاویر یا اسناد با کیفیت بالا و ارسال اعلانهای انبوه است.