مقیاس پذیری در NodeJs
Cpu intensive :
منظور از پردازش هایی که cpu را درگیر میکند (CPU intensive) پردازش هایی مانند:
رمزنگاری داده ها، پردازش (تصویر/صدا/ویدیو) و همچنین محاسبات سنگین ریاضی، مدل های یادگیری ماشین و … این نوع پردازش ها پردازش های سنگینی برای NodeJs میباشد.
ناهمزمان یا asynchronous :
ناهمزمان یا asynchronous بودن nodejs بدین معناست که میتواند چندین درخواست را بطور همزمان مدیریت کند اما فقط برای عملیات های I/O مانند درخواست های HTTP، عملیات های کار با fs (فایل سیستم)، اجرای کوئری های دیتابیس، برنامه های ریل تایم مانند چت اپلیکیشن و … مناسب میباشد.
نکته: برای پردازش یا درخواست هایی که cpu را درگیر میکند، خود nodejs پکیج هایی را برای بهتر مدیریت کردن درخواست ها و انجام پردازش ها ارائه میدهد.
توی این مقاله می خواهیم انواع روش های بالا بردن مقیاس پذیری اپلیکیشن های Nodejs را معرفی و مورد بررسی قرار دهیم.
کش کردن داده ها در Nodejs
(Redis Cache)
Redis چیست؟
کلمه Redis مخفف عبارت Remote Dictionary Server است. به بیان سادهتر دیتابیس Redis نوعی ساختمان داده میباشد که اطلاعات را در RAM نگهداری میکند. این کار باعث میشود که سرعت دسترسی به اطلاعات بسیار بالاتر باشد. بالا بودن سرعت بزرگترین مزیت و تفاوت اصلی دیتابیس redis با دیگر دیتابیس های دیگر است. با توجه به اینکه اطلاعات داخل RAM نگهداری میشود، آن را In-Memory Database مینامند. پس Redis یک دیتابیس جدا از دیتابیس اصلی میباشد و تمام دیتاها را در حافظه نهان ذخیره میکند.
کاربرد استفاده از Redis
اگر ما مجموعه ای از داده ها را در زمان لود برنامه به طور مداوم دریافت کنیم و این باعث میشود که به ازای هر بار ارسال ریکوئست یک کوئری دیتابیس اجرا شده و دیتا ها دریافت و به سمت کلاینت ارسال شود اما کار بهینه ای که باید انجام شود این است که با استفاده از Redis دیتا ها را ذخیره و کش کنیم.
پس در مرحله ی اول باید redis را روی سیستم خود نصب کنیم و با دستورات زیر redis را اجرا و استفاده کنیم
sudo service redis-server start
sudo service redis-server-start
Redis-cli
set myKey "Hello"
get myKey
حالا برای استفاده از Redis در NodeJs کافیه که پکیج redis را نصب کنید، بعد از نصب دستورات زیر را دنبال کنید :
import redis from 'redis'
const URL = process.env.REDIS_URL || 'redis://localhost:6379'
const client = redis.createClient({ url: URL })
await client.connect()
client.on('error', (error) => {
console.error('Redis client error:', error);
});
client.on('connect', (err) => {
console.log('Connected to redis')
})
export default client
با دستورات فوق میتوانید به دیتابیس redis متصل میشوید و با import کردن redisClient در سراسر برنامه ها میتوانیم یک سری کلید را با مقادیر دلخواه مقداردهی کنیم :
await client.set('mykey', 'hello');
پس میدانیم که با کمک ردیس میتونایم دیتا های سمت سرور را کش کنیم و سرعت دسترسی به دیتا ها همچنین سرعت پاسخ به درخواست ها را افزایش داده و همچنین ردیس تنها راه حل برای افزایش سرعت و سرور نیست بلکه میتوانیم از node-cache استفاده کنیم که در ادامه به آن میپردازیم:
Node Cache
استفاده از این پکیج به سادگی استفاده از redis میباشد اما تفاوت هایی باهم دارند که مورد بررسی قرار دهیم.
دیتابیس redis یک دیتابیسی میباشد که میتوان با استفاده از برنامه های دیگری بهش متصل شده و دسترسی پیدا کرد اما NodeCache فقط در آن برنامه ی NodeJs در دسترس میباشد، برای شروع ابتدا پکیج node-cache را نصب می کنیم:
import NodeCache from "node-cache"
const myCache = new NodeCache()
//ذخیره کردن یک کلید
const result1 = myCache.set("myKey1", "hello");
// ذخیره کردن چندین کلید یا دیتا
const obj = { my: "Special", variable: 42 };
const obj2 = { my: "other special", variable: 1337};
const result2 = myCache.mset([
{
key: "myKey2",
val: obj,
ttl: 10000
},
{
key: "myKey3",
val: obj2
},
])
const value = myCache.get("myKey1");
پس نحوه ی کار کردن با Redis و node-cache را یاد گرفتیم حالا میپرازیم به مباحث دیگه ای که میتوانیم برای مقیاس پذیری و بالا بردن ظرفیت اپلیکیشن های nodejs ی میتوان استفاده کرد
Cluster یا خوشه بندی
چرا خوشه بندی؟
ما زمانی که یک برنامه ی node js را اجرا میکنیم که درخواست ها را دریافت و مدیریت کند، حالا فرض کنید که ما ۸ تا کپی یا instance از برنامه ای که اجرا کردیم داشته باشیم و با استفاده از load balancer ریکوئست ها را روی تمامی برنامه ها توزیع کنیم که باعث میشود سرعت و ظرفیت اپلیکیشن ما به تعداد instance هایی که میسازیم n برابر شود و این کار به عهده ی cluster میباشد.
پکیج cluster یک پکیج داخلی nodejs به اصطلاح core-module میباشد و نیازی به نصب نیست. یک مثال از یک برنامه ی اکسپرسی را با کمک cluster باهم برسی میکنیم:
import express from "express"
const app = express()
const PORT = process.env.PORT || 5000
app.listen(PORT, () => console.log(`Server is running successfully on PORT ${PORT}`))
نکته: ما تعداد برنامه هایی که اجرا میکنیم به تعداد thread های cpu سرور یا سیستم شما مرتبط است
import cluster from 'cluster'
const numCpu = os.cpus().length;
if (cluster.isPrimary) {
console.log(`Primary ${process.pid} is running`);
for (let i = 0; i < numCpu; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`${worker.process.pid} has exited`);
cluster.fork();
})
} else {
app.listen(PORT, () => console.log(`Server ${process.pid} is running successfully on PORT ${PORT}`))
}
به طور مثال در کد فوق ما تعداد thread های cpu رو دریافت میکنیم و به تعداد thread های مورد نظر یک worker جدید را مشغول (trigger) میکند یا بخوام به طور مختصر بگم با یک قلاب worker یا کارگر را وارد میکنیم و worker اصلی یا Primary Worker به همه ی connection ها گوش میدهد و سپس بار را بین سایر worker ها با روش یا الگوریتم RR یا Round-Robin توزیع میکنم.
یک توضیح مختصر به Round-Robin داشته باشیم:
الگوریتم Round-Robin یک الگوریتم زمان بندی میباشد که برای سیستم های اشتراک زمانی طراحی شده یا بطور ساده تر وظایف را فقط در بین مجموعه ای از منابع سرور های موجود توزیع میکند این الگوریتم در دسته الگوریتمهای پیشگیرانه قرار گرفته و بلادرنگ می باشد زیرا در یک محدوده زمانی خاص به رویداد پاسخ میدهد.
خب در اینجا هم یاد گرفتیم که چطور میتوان با کمک cluster نمونه های بیشتری از اپلیکیشن خود داشته باشیم.
Worker Threads
این پکیج مانند cluster یکی از پکیج های ارائه شده توسط خود node Js میباشد
یک نکته ای که به عنوان یک توسعه دهنده ی nodejs باید بدانید این است که node js به صورت تک نخی یا single thread میباشد و این بدین معناست که node ما تنها یک نمونه یا instance از آن در حال اجراست و روی یک thread اصلی قرار گرفته و این thread تمامی ریکوئست ها و و پردازش ها را دریافت و به ترتیب اجرا میکند. این thread اصلی event loop نامیده میشود که به زبان فارسی به آن حلقه ی رویداد میگویند که من پیشنهاد میکنم همان event loop صداش بزنید. Event loop مسئولیت تمامی عملیات های asynchronous و عملیات های I/O مانند کار با فایل سیستم و همچنین اجرای کوئری های دیتابیسی را بر عهده دارد.
اما تک نخی یا single thread هم مزایا و معایب خودش را دارد بطور مثال اگر در زمان پردازش یک task خطایی رخ بدهد باعث متوقف شدن پردازش های thread میشود اما اگر سیستم چند نخی یا multi thread باشد بقیه ی پردازش ها کار خودشان را انجام میدهند و فقط thread ی که در آن خطا به وجود آمده متوقف خواهد شد اما این پایان کار نیست nodejs این امکان را به ما میدهد که برنامه ی خود را با کمک پکیج worker_threads چند نخی کنیم و یا چندین thread را به برنامه ی خود اضافه کنیم کد زیر را برای درک بهتر دنبال کنید:
import { Worker } from "worker_threads"
const makeCalculation = async (req, res) => {
try {
const worker = new Worker('./worker.js', { workerData: { num: 10 } })
worker.on('message', (message) => {
if (message.success) {
res.send({
message: 'Successfully calculated',
success: true,
ans: message.ans
})
} else {
res.send({
message: 'Calculation not possible',
success: false
})
}
})
} catch (error) {
console.log('Error 7: ', error)
}
}
export default makeCalculation;
فایل worker.js:
import { parentPort, workerData } from 'worker_threads';
const ans = workerData.num * 900000
parentPort.postMessage({message: 'Successfully calculated', success: true, ans: ans})
روش کار بدین صورت است که thread اصلی ما یک worker جدید را با استفاده از متد Worker() ایجاد کرده که پارامتر های این متد بدین شکل است:
پارامتر اول : اسم فایلی که کد یا اسکریپت ما در اون قرار گرفته برای اجرا
پارامتر دوم: دیتاهایی که برای این فایل ارسال میکنیم
در واقع فایل worker.js میتواند یک قطعه برنامه با پردازش سنگین باشد که توسط worker_threads در یک thread دیگر از سیستم عامل اجرا شده و thread اصلی یا event loop را درگیر پردازش این فایل نمیکند.
نتیجه گیری:
ما در این بخش از مقاله فهمیدیم با کمک روش های فوق میتوانیم پرفورمنس و مقیاس پذیری اپلیکیشن ها خود را افزایش دهیم و منتظر مقالات بعدی در این رابطه باشید.
از سال ۸۹ - ۹۰ وارد حوزه ی برنامه نویسی شدم و انواع زمینه ها و شاخه های مختلف رو کار کردم تا اینکه سال ۹۵ توی حوزه ی بک اند (نود جی اس) ماندگار شدم، تجربیات خیلی زیادی رو توی این مسیر کسب کردم. شکست ها و موفقیت هایی رو هم داشتم که همه ی این موارد رو در قالب مقاله، دوره و پادکست در اختیارتون خواهم گذاشت خلاصه که وبسایت کدنایت رو سال ۱۴۰۲ توسعه دادیم که یک پلتفرم آموزشی با گروهی از اساتید خفن هستش که قراره کلی محتوا در اختیارتون بذاریم.