امروز : ۲۶ خرداد ۱۴۰۵ (2026/06/16)

17 تکنیک حرفه‌ ای برای افزایش سرعت NodeJS

افزایش سرعت NodeJs؛ راهنمای کامل بهینه‌ سازی در پروژه‌ های واقعی

Nodejs طی سال های اخیر به یکی از محبوب ترین فناوری های توسعه بک اند تبدیل شده است و شرکت های بسیار بزرگی در دنیا مانند Netflix، PayPal و LinkedIn از Nodejs برای پردازش میلیون ها درخواست در طی روز استفاده می کنند.

نباید فراموش کنیم در دنیای توسعه وب انتخاب فناوری مناسب برای بخش Backend تاثیر مستقیمی بر سرعت، مقیاس پذیری و موفقیت پروژه دارد.

در این میان Nodejs توانسته در میان توسعه دهندگان جایگاه بسیار خوبی پیدا کند این فناوری به برنامه نویسان اجازه می دهد تا با استفاده از زبان جاوا اسکریپت علاوه بر بخش Frontend، سرور سرور را نیز توسعه دهند و از یک زبان مشترک در کل پروژه استفاده می کنند.

ما قبلا در مقاله nodejs پلی میان فرانت و بک اند! در این خصوص صحبت کردیم و از مزیت هایی که انتخاب Nodejs برای فرانت و بک اند دارد دلایلی را ذکر کردیم که می توانید مطالعه بفرمایید.

Nodejs بر پایه موتور V8 گوگل توسعه یافته و با بهره‌ گیری از معماری Event-Driven و مدل Non-Blocking I/O، امکان پردازش تعداد زیادی درخواست همزمان را فراهم می‌ کند. همین ویژگی باعث شده است که برای توسعه API ها، برنامه‌ های Real-Time، سرویس‌ های مبتنی بر Microservice و اپلیکیشن‌ های مقیاس‌پذیر گزینه‌ ای بسیار مناسب باشد.

نحوه پیاده‌ سازی پروژه، مدیریت منابع، ساختار کد ها و بهینه‌ سازی بخش‌ های مختلف سیستم نقش مهمی در سرعت و کارایی نهایی برنامه دارند. به همین دلیل آشنایی با تکنیک‌ های افزایش سرعت و بهینه‌ سازی عملکرد Nodejs برای هر توسعه‌ دهنده‌ ای که قصد ساخت برنامه‌ های حرفه‌ای را دارد، ضروری است.

نود جی اس با وجود سرعت بسیار بالایی که در پردازش درخواست ها دارد. اما بسیاری از توسعه دهندگان پس از راه اندازی مشکلاتی مانند کندی API ها، مصرف زیاد حافظه، افزایش زمان پاسخگویی و ناتوانی در مدیریت کاربران همزمان را که نود جی اس برای آن ساخته شده است را گزارش کرده اند برای همین افزایش سرعت NodeJS یکی از مواردی هست که حتما باید بررسی شود.

در چنین شرایطی شاید خیلی ها تصور کنند صرفا با افزایش منابع سخت افزاری می توانند سرعت را بهبود ببخشند و وضعیت را پایدار کنند! این کار شاید یک راه حل به نظر برسد اما راهکار های دیگری هم وجود دارد که می توان عملکرد برنامه را بهینه تر کرد و از این موضوع جلوگیری کرد.

در این مقاله ما می خواهیم در مورد مهم ترین روش ها و راهکار هایی عملی که کمک می کند افزایش سرعت NodeJS بهبود پیدا کند و درخواست های بالاتری را پاسخ دهد صحبت کنیم و راهکار هایی را خدمت شما عرض کنیم.

 

 

Node.js چگونه کار می‌ کند؟

اجازه دهید قبل از اینکه به سراغ روش هایی برای افزایش سرعت و عملکرد نود جی اس برویم ابتدا با نحوه کار آن آشنا شویم!

درک معماری داخلی Nodejs به ما کمک می کند تا دلیل بسیاری از مشکلات عملکرد سیستم را بهتر متوجه شویم و راهکارهایی که انتخاب می کنیم متناسب تر باشد تا مشکل به طور کامل حل شود.

بسیاری از زبان ها و فریم ورکهای سمت سرور در زمان پردازش درخواست ها برای هر درخواست یک Thread جداگانه باز می کردند و این مورد باعث می شد وقتی تعداد درخواست ها بالا می رود تعداد Thread های زیاد صف تشکیل می داد و منابع سرور را به شدت درگیر می کرد و سیستم کاملا کند می شد و در درخواست های خیلی زیاد اگر منابع سخت افزاری کافی نبود و بهینه سازی های اصولی انجام شده نبود سیستم کاملا داون می شد!

اما نود جی اس با معماری Event-Driven و مدل Non-Blocking I/O که استفاده می کند توانست تحولی در این خصوص ایجاد کند و این روش باعث شد برنامه بتواند تعداد بالایی از درخواست ها را به طور همزمان بررسی و پاسخ دهد بدان آنکه نیاز باید برای هر درخواست یک Thread جداگانه باز کند. همین ویژگی باعث محبوبیت بالای Nodejs در توسعه API  و برنامه های Real-time شد! امروزه بسیاری ازشرکت های بزرگ دنیا برای قسمت هایی از سیستم خود که نیاز به پاسخگویی در لحظه بسیار بالا دارند بدون شکل از نود جی اس استفاده می کنند.

اما واقعا چه عاملی باعث می شود نود جی اس با وجود بالا بودن تعداد درخواست های همزمان همچنان کارایی بسیار خوبی داشته باشد و کند نشود؟

در مرکز این معماری مفهومی قرار دارد به نام Event Loop ! این مفهوم در واقع مکانیزمی است که وظیفه مدیریت زمان بندی و اجرای عملیات های مختلف را بر عهده دارد! زمانی که یک درخواست به سرور ارسال می شود Nodejs آن را دریافت می کند و وارد چرخه Event Loop می کند و اگر درخواست شامل عملیات‌ های ورودی و خروجی مانند خواندن فایل، ارتباط با دیتابیس یا ارسال درخواست به یک سرویس خارجی باشد، این عملیات‌ ها به سیستم‌ عامل یا Thread Pool واگذار می‌ شوند تا به صورت غیر همزمان پردازش شوند.

اگر بخواهیم به صورت خلاصه فرآیند پردازش درخواست ها در نود جی اس را توضیح دهیم باید به شکل زیر بگوییم:

درخواست وارد Event Loop می‌ شود.

عملیات‌ های ورودی و خروجی به سیستم‌عامل یا Thread Pool سپرده می‌ شوند.

Thread اصلی آزاد می‌ ماند و می‌ تواند درخواست‌ های جدید را دریافت کند.

پس از پایان عملیات، نتیجه به Event Loop بازگردانده می‌ شود.

Callback، Promise یا تابع Async مربوطه اجرا شده و پاسخ برای کاربر ارسال می‌ شود.

این مدل پردازش دقیقا همان عاملی هست که باعث می شود نود جی اس بتواند با حداقل مصرف منابع تعداد بسیار بالایی از درخواست ها را به صورت همزمان مدیریت کند! به همین دلیل در بسیاری از برنامه های مبتنی بر API، سیستم‌ های چت آنلاین، سرویس‌ های استریم و اپلیکیشن‌ های Real-Time عملکرد بسیار خوبی از خود نشان می‌ دهد.

ما به صورت مفصل قبلا در مقاله Event Loop چیست و چرا Nodejs هنگ نمی کند! این موضوع را بررسی کردیم!

با این حال این معماری به این صورت نیست که هیچ وقت قرار نیست مشکلی رخ دهد! از آنجا که بخش عمده پردازش ها توسط یک Thread اصلی مدیریت می شود هر عملیاتی که زمانی زیادی از CPU را اشغال کند می تواند Event Loop را متوقف یا اصطلاحا Block کند. در چنین شرایطی، Nodejs دیگر قادر به رسیدگی سریع به درخواست‌ های جدید نخواهد بود و کاربران با افزایش زمان پاسخ‌ گویی یا حتی Timeout مواجه می‌ شوند.

این دقیقا همان دلیلی هست که باعث می شود نود جی اس برای اجرای محاسبات پیچیده که سی پیو محور هستند، پردازش تصاویر بزرگ، فشرده سازی فایل های حجیم و یا استفاده از حلقه های سنگین در پروژه مناسب نباشد و Event Loop برای مدت قابل توجهی مشغول بماند و نتواند به درخواست های دیگر پاسخ دهد!

به همین دلیل یکی از مهم ترین اصول بهینه سازی در نود جی اس در واقع جلوگیری از مسدود شدن Event Loop و انتقال پردازش سنگین به Worker Threads یا سرویس‌ های جداگانه است.

درک نحوه عملکرد Event Loop و معماری Non-Blocking I/O پایه و اساس بهینه‌ سازی Nodejs محسوب می‌ شود. بسیاری از تکنیک‌ هایی که در ادامه این مقاله بررسی خواهیم کرد، با هدف آزاد نگه داشتن Thread اصلی، کاهش فشار روی Event Loop و استفاده بهینه از منابع سیستم طراحی شده‌ اند تا برنامه بتواند در شرایط پرترافیک نیز عملکرد سریع و پایداری داشته باشد.

 

دلایل اصلی کند شدن Nodejs:

درست است که نود جی اس به عنوان یکی از سریع ترین و کارآمد ترین فناوری های بک اند شناخته می شود اما مانند هر پروژه دیگری ممکن است پس از مدتی با مشکلاتی نظیر کندی رو به رو شود! در این شرایط معمولا توسعه دهندگان تصور می کنند این کندی ناشی از مشکلات خود نود جی اس یا محدودیت های سرور و یا هاست Nodejs می باشد! در حالی که در اکثر موارد علت اصلی به نحوه طراحی و پیاده سازی پروژه باز می گردد.

افت سرعت و کاهش عملکرد پروژه معمولا به صورت تدریجی رخ می دهد یعنی ممکن است در ابتدای توسعه همه چیز به خوبی کار کند و هیچ مشکلی دیده نشود اما به مرور با افزایش تعداد کاربران و رشد حجم داده ها و پیچیده تر شدن منطق برنامه در کنار اضافه شدن ویژگی های جدید به پروژه باعث می شود نقاط ضعف سیستم کم کم خود را نشان دهند! نتیجه این مشکلات می‌ تواند افزایش زمان پاسخ‌گویی API ها، مصرف بیش از حد CPU و حافظه، ایجاد صف در درخواست‌ ها و در نهایت کاهش تجربه کاربری باشد.

در نهایت، معماری نامناسب پروژه می‌ تواند زمینه‌ ساز بسیاری از مشکلات عملکردی باشد. ساختار نامنظم کدها، وابستگی بیش از حد بین بخش‌ های مختلف سیستم، مدیریت ضعیف منابع و انتخاب نادرست الگو های طراحی، همگی می‌ توانند در بلند مدت باعث کاهش کارایی و دشوار شدن فرآیند بهینه‌ سازی شوند.

در ادامه مقاله هر یک از این عوامل را به صورت دقیق‌ تر بررسی کرده و راهکارهای عملی برای رفع آن‌ ها را معرفی خواهیم کرد.

 

1. حذف عملیات Sync:

یکی از دلایل مهمی که عملکرد برنامه های نود جی اس کاهش پیدا می کند این است که عملیات همزمان یا همان Synchronous در بخش های مختلف برنامه استفاده می شود.

زمانی که یک عملیات Sync اجرا می شود Thread اصلی تا زمان پایان آن عملیات مسدود باقی می ماند و امکان رسیدگی به سایر درخواست ها را ندارد! بنابراین کندی شدید رخ خواهد داد.

در پروژه های کوچک ممکن است این مورد چندان محسوس نباشد اما وقتی کاربران افزایش پیدا می کنند و باعث بالا رفتن حجم درخواست می شود این مورد یک معضل جدی تبدیل می شود! هرچند مدت زمان اجرای عملیات Sync بیشتر باشد تاخیر در پاسخگویی نیز افزایش پیدا می کند و در نتیجه کاربران با کندی و قطعی بیشتری مواجه می شوند.

عملیات‌ هایی مانند خواندن و نوشتن فایل‌ ها، پردازش داده‌ های حجیم و برخی از توابع داخلی Nodejs در نسخه‌ های Sync خود می‌ توانند عملکرد کل سرور را تحت تاثیر قرار دهند. به همین دلیل در محیط‌ های Production توصیه می‌ شود تا حد امکان از نسخه‌ های Asynchronous این توابع استفاده شود تا Event Loop آزاد باقی بماند و بتواند سایر درخواست‌ ها را نیز پردازش کند.

مثال نامناسب:

const fs = require('fs');

const data = fs.readFileSync('users.json');

در این حالت تا پایان خواندن فایل، هیچ درخواست دیگری پردازش نمی‌شود.

مثال مناسب:

const fs = require('fs/promises');

const data = await fs.readFile('users.json');

در این روش که مشاهده می کنید عملیات خواندن فایل به صورت غیرهمزمان مدیریت می شود و Thread همچنان می تواند در همین زمان به سایر درخواست ها پاسخ دهد.

توابع Sync برای اسکریپت‌ های کوچک، ابزار های خط فرمان یا همان CLI یا عملیات‌ هایی که تنها یک بار هنگام Startup برنامه اجرا می‌ شوند مناسب هستند، اما استفاده از آن‌ ها در مسیر پردازش درخواست‌ های کاربران می‌ تواند به مرور زمان باعث افت شدید عملکرد سرور شود. به همین دلیل یکی از اولین اقدامات در بهینه‌ سازی پروژه‌ های Nodejs، شناسایی و حذف عملیات Sync از بخش‌ های پرترافیک برنامه است.

2- استفاده صحیح از Async/Await:

استفاده از شیوه Async/Await یکی از روش های بسیار مرسوم و عالی برای مدیریت عملیات های غیرهمزمان در برنامه های نود جی اس می باشد! این قابلیت باعث می شود کدها خواناتر و نزدیک تر به ساختار کدهای همزمان باشند! با این حال تنها با استفاده از Async/Await نمی توان انتظار داشت عملکرد بهتر شود و در بسیاری از پروژه ها نحوه استفاده نادرست از آن می تواند باعث ایجاد تاخیر های غیرضروری و کاهش سرعت پاسخگویی شود!

یکی از اشتباهات رایج توسعه دهندگان این است که چند عملیات مستقل را به صورت متوالی انجام می دهند در حالی که هیچ وابستگی بین آن ها وجود ندارد! در چنین شرایطی هر عملیات منتظر پایان عملیات قبلی می ماند و در مجموع زمان اجرای همه آن ها برابر با مجموع تک تک آن های می شود!

این موضوع در API هایی که چندین Query دیتابیس، درخواست سرویس خارجی یا عملیات I/O را همزمان انجام می‌ دهند، تأثیر قابل توجهی بر عملکرد سیستم خواهد داشت.

برای جلوگیری از این مشکل ابتدا باید تشخیص دهید چه عملیات هایی مستقل از یکدیگر هستند و نیاز نیست منتظر بمانند تا قبلی به پایان برسد! و به صورت همزمان آن ها را اجرا کرد تا زمان انتظار به حداقل برسد! و همچنین باعث می شود استفاده از منابع سیستم بهینه تر شود!

نمونه استاندارد:

const user = await User.findById(id);

استفاده از Await برای یک عملیات مستقل کاملا مناسب و خوانا است.

نمونه ضعیف:

const user = await getUser();
const posts = await getPosts();
const comments = await getComments();

در این مثال هر درخواست پس از پایان درخواست قبلی اجرا می‌ شود. اگر هر عملیات یک ثانیه زمان ببرد، مجموع زمان اجرای کد حدود سه ثانیه خواهد بود.

نمونه بهتر:

const [user, posts, comments] = await Promise.all([
  getUser(),
  getPosts(),
  getComments()
]);

در این روش تمام عملیات‌ ها به صورت همزمان آغاز می‌ شوند و برنامه فقط یک بار منتظر دریافت نتایج می‌ ماند و همین مورد باعث می شود سرعت انجام سریعتر باشد.

3- استفاده از Redis Cache:

یکی دیگر هم از روش های موثر افزایش سرعت برنامه های نود جی اس استفاده از سیستم کش می باشد! در بسیاری از پروژه های بخش قابل توجهی از درخواست ها مربوط به داده هایی هست که بارها توسط کاربران مختلف درخواست می شود اما محتوای آن ها در بازه های زمانی کوتاه تغییر نمی کند! در این شرایط استفاده ازسیستم کش کمک می کند هر بار نیاز نباشد برای مثال درخواست مجدد به دیتابیس ارسال شود و پاسخ تکراری را دریافت کند!

این کار علاوه بر اینکه زمان پاسخگویی را کاهش می دهد در واقع فشار زیادی را از روی سرور و پایگاه داده برمیدارد!

اگر از سیستم کش استفاده نشود با رشد تعداد کاربران این مشکل بیشتر خود را نشان می دهد و اجرای مداوم Query های تکراری باعث افزایش مصرف CPU و RAM می شود و منابع دیتابیس را هم درگیر می کند و به یکی از معضلات اصلی سیستم بدل می شود!

در چنین شرایطی استفاده از یک لایه Cache میان برنامه و دیتابیس می‌ تواند بخش بزرگی از این بار اضافی را حذف کند.

Redis یکی از محبوب‌ ترین ابزارهای Cache در Nodejs است که داده‌ ها را در حافظه RAM نگهداری می‌ کند و به همین دلیل سرعت دسترسی بسیار بالاتری نسبت به دیتابیس‌ های معمولی دارد.

ایده اصلی این است که ابتدا داده از Cache بررسی شود و تنها در صورتی که اطلاعات مورد نظر وجود نداشته باشد، درخواست به دیتابیس ارسال شود. این رویکرد باعث کاهش تعداد Query ها و افزایش سرعت API ها می شود.

فرض کنید اطلاعات یک محصول روزانه هزاران بار درخواست می‌ شود.

اگر بخواهیم هر بار به دیتابیس مراجعه کنیم هم بار اضافی به دیتابیس وارد می شود و هم سرعت پاسخگویی کند می شود!

اما با Redis:

const cache = await redis.get(key);

if(cache){
  return JSON.parse(cache);
}

ابتدا کش بررسی می‌ شود و فقط در صورت نبود داده، Query اجرا خواهد شد!

مواردی که می توانید در کش استفاده کنید شامل اطلاعات کاربران، محصولات، دسته بندی ها، تنظیمات سایت، آمار و گزارش و … می باشد.

 

4- Rate Limiting:

هرچه قدر روی بهینه سازی داخلی کد ها و دیتابیس هم کار انجام شود اما بخش مهم که عملکرد کلی سیستم را حفظ می کند جلوگیری از فشار بیش از حد به سرور هست. حتی اگر تمام Query ها و Cache و ساختار کد بهینه باشد باز هم تعداد بالای درخواست در بازه زمانی های کوتاه می تواند باعث افت عملکرد جدی یا حتی از دسترس خارج شدن کل سرویس شود!

Rate Limiting در واقع مکانیزمی برای کنترل تعداد درخواست‌ های ورودی از سمت هر کاربر یا IP در یک بازه زمانی مشخص است. این روش کمک می‌ کند تا از ارسال درخواست‌ های بیش از حد چه به صورت ناخواسته و چه عمدی جلوگیری شود و سرور بتواند منابع خود را به شکل عادلانه بین کاربران تقسیم کند.

این تکنیک نه تنها برای بهبود عملکرد اهمیت دارد، بلکه نقش مهمی در افزایش امنیت و پایداری سیستم نیز ایفا می‌ کند. بسیاری از حملات ساده مانند Brute Force یا Flooding با استفاده از Rate Limiting تا حد زیادی قابل کنترل هستند.

مثال:

const rateLimit = require('express-rate-limit');

app.use(
  rateLimit({
    windowMs: 15 * 60 * 1000,
    max: 100
  })
);

در این مثال، هر کاربر در بازه زمانی 15 دقیقه تنها مجاز به ارسال 100 درخواست است.

مزایا:

جلوگیری از Abuse و استفاده بیش از حد منابع

کاهش اثر حملات ساده مانند Brute Force و Request Flood

حفظ پایداری و ثبات سرور در زمان ترافیک بالا

توزیع عادلانه منابع بین کاربران

کاهش احتمال Down شدن سرویس

در پروژه‌ های پروداکشن معمولاً Rate Limiting در لایه‌ های مختلف اعمال می‌ شود. این چند لایه بودن باعث می‌ شود کنترل دقیق‌ تری روی ترافیک ورودی وجود داشته باشد و سیستم در برابر بار های غیرعادی مقاوم‌ تر شود.

 

5- بهینه‌ سازی Query های MongoDB:

در بررسی هایی که از پروژه های نود جی اس انجام می شود تصور کندی در ابتدا از مشکلاتی نظیر خود برنامه یا سرور می باشد اما در عمل بخش بزرگی از مشکلات که این پروژه ها با آن رو به می شوند نحوه تعامل با دیتابیس مرتبط هست!

حتی اگر کدی های نود جی اس کاملا بهینه باشد اجرای Query های سنگین و غیربهینه می تواند زمان پاسخگویی را به شکل مضاعفی افزایش دهد!

MongoD‌‌B جزو دیتابیس هایی می باشد که می تواند حجم بسیار زیادی از داده ها را مدیریت کند اما این موضوع به این معنی نیست که تمام Query ها قرار است سریع باشد!

زمانی که درخواست ها بدون Index مناسب اجرا می شوند حجم زیادی از اطلاعات غیرضروری از دیتابیس دریافت می شود که باعث می شود دیتابیس منابع بیشتری را برای هر پردازش مصرف کند و با افزایش تعداد رکورد ها این مشکلات به تدریج بزرگتر هم می شود و سرعت پاسخگویی کاهش شدیدی پیدا می کند!

بهینه‌ سازی Query ها نه تنها باعث کاهش زمان پاسخ API ها می‌شود، بلکه مصرف CPU، حافظه و منابع سرور دیتابیس را نیز کاهش می‌ دهد.

یکی از مهم‌ ترین تکنیک‌ های بهینه‌ سازی در MongoDB، ایجاد Index روی فیلدهایی است که به صورت مداوم در عملیات جستجو، فیلتر، مرتب‌ سازی یا Join مورد استفاده قرار می‌ گیرند.

بدون Index:

User.findOne({
  email: "user@example.com"
});

در این حالت MongoDB ممکن است مجبور شود بخش بزرگی از Collection یا حتی تمام آن را بررسی کند تا رکورد مورد نظر را پیدا کند.

اما با Index:

db.users.createIndex({
  email: 1
});

پس از ایجاد Index، عملیات جستجو با سرعت بسیار بیشتری انجام می‌ شود و فشار کمتری به دیتابیس وارد خواهد شد.

دریافت فیلد های مورد نیاز:

یکی دیگر از اشتباهات رایجی که در بسیاری از پروژه های نود جی اس که از دیتابیس مونگو استفاده می کنند دیده می شود این است که اطلاعاتی را دریافت می کنند که هیچ استفاده ای در منطق برنامه یا پاسخ API ندارد!

زمانی که یک Query تمام فیلد های یک سند را برمیگرداند حجم بیشتری از داده از دیتابیس خوانده می شود و حافظه بیشتری مصرف می شود و زمان بیشتری برای تبدیل داده ها به JSON و ارسال آن ها از طریق شبکه صرف خواهد شد.

این موضوع در Collection های کوچک شاید تاثیر محسوسی نداشته باشد، اما زمانی که اسناد شامل فیلد های متعدد، داده‌ های حجیم یا آرایه‌ های بزرگ باشند، دریافت اطلاعات غیرضروری می‌تواند به یکی از عوامل اصلی کاهش سرعت API ها تبدیل شود.

به همین دلیل یکی از اصول مهم بهینه‌ سازی Query ها این است که فقط فیلد هایی را دریافت کنیم که واقعا مورد نیاز هستند. این کار علاوه بر کاهش حجم داده‌ های منتقل‌ شده، باعث بهبود زمان پاسخ‌ گویی و کاهش مصرف منابع سیستم می‌ شود.

اشتباه:

User.find({});

بهتر:

User.find({}, 'name email');

در این روش فقط فیلدهای name و email از دیتابیس دریافت می‌ شوند و سایر اطلاعات نادیده گرفته می‌ شوند.

استفاده از Pagination:

یکی از مشکلات رایج در پروژه‌ هایی که با حجم زیادی از داده‌ ها سروکار دارند، دریافت تمام رکورد ها در یک درخواست است!

درست است که در مراحل اولیه توسعه مشکلی ایجاد نمی کند اما به مرور زمان باعث کاهش عملکرد سیستم می شود! هرچه تعداد رکورد های بازگشتی بیشتر باشد، دیتابیس زمان بیشتری برای پردازش درخواست صرف می‌ کند و همچنین حافظه بیشتری در سمت سرور و کلاینت مصرف می‌ شود.

به همین دلیل در سیستم های واقعی داده ها را به صورت صفحه بندی شده یا همان Pagination دریافت می کنند! در این روش تنها تعداد مشخصی از رکورد ها در هر درخواست فراخوانی می شود و کاربر برای مشاهده اطلاعات بیشتر صفحات بعدی را درخواست می کند!

این کار باعث کاهش مصرف منابع و افزایش سرعت پاسخگویی می شود.

اشتباه:

Post.find();

در این حالت تمام رکورد های موجود در Collection دریافت می‌ شوند که در داده‌ های حجیم می‌ تواند فشار زیادی به دیتابیس و سرور وارد کند.

بهتر:

Post.find()
  .skip(0)
  .limit(20);

در این روش فقط 20 رکورد اول دریافت می‌ شود و از ارسال حجم زیادی از اطلاعات جلوگیری خواهد شد.

6- استفاده از Compression:

یکی دیگر از روش های ساده اما خیلی موثر برای افزایش سرعت بارگذاری صفحات و API ها فشرده سازی پاسخ های HTTP می باشد!

زمانی که سرور اطلاعات را برای مرورگر یا کلاینت ارسال می کند حجم داده ها مستقیما بر زمان انتقال آن از طریق شبکه تاثیر می گذارد! هرچه حجم کمتر باشد اطلاعات سریعتر منتقل می شود و کاربر هم در مدت زمان کوتاه تری نتیجه را دریافت خواهد کرد!

در بسیاری از پروژه‌ ها، بخش قابل توجهی از حجم پاسخ‌ ها مربوط به داده‌ های متنی مانند JSON، فایل‌ های CSS و JavaScript به همراه HTML است.

این نوع داده‌ ها معمولاً قابلیت فشرده‌ سازی بسیار بالایی دارند و می‌ توان حجم آن‌ها را بدون تغییر در محتوا به میزان قابل توجهی کاهش داد. در نتیجه، هم مصرف پهنای باند کاهش پیدا می‌ کند و هم زمان بارگذاری برای کاربران کمتر می‌ شود.

امکان پیاده سازی Compression در نود جی اس بسیار ساده می باشد و تنها با استفاده از یک Middleware می توان آن را برای تمام پاسخ های برنامه فعال کرد!

const compression = require('compression');

app.use(compression());

با فعال شدن Compression، پاسخ‌ های HTTP قبل از ارسال فشرده شده و در سمت کلاینت به صورت خودکار از حالت فشرده خارج می‌ شوند.

7- استفاده از Cluster Mode:

وقتی توسعه دهندگان نود جی اس شروع به بهینه سازی می کنند با یکی از نکات مهمی آشنا می شوند که شاید در زمان توسعه به آن بی توجه هستند!

نحوه استفاده این پلتفرم از منابع پردازشی سرور به صورت پیشفرض روی یک Thread اصلی اجرا می شود و عملا از یک هسته سی پیو استفاده می کند این موضوع باعث می شود حتی سرور هایی که چندین هسته پردازشی دارند بخش زیادی از توان سخت افزاری خود را در پروژه به کار نگیرند!

وقتی پروژه ترافیک پایینی دارد سرعت بالاست و این محدودیت یک هسته اصلا مشخص نیست چون سرعت پاسخگویی به گونه ای نیست که کاربر احساس کندی کند اما وقتی تعداد کاربران و درخواست ها بالا می رود تازه افت سرعت شکل می گیرد در حالیکه خود سرور همچنان توان بالایی برای پردازش دارد اما عملا استفاده نمی شود.

برای حل این موضوع خود Nodejs قابلیت Cluster Mode را در اختیار توسعه دهندگان قرار می دهد! این قابلیت امکان اجرای چندین فرآیند مستقل از برنامه را فراهم می کند که هر کدام روی یک هسته پردازنده اجرا می شوند در نتیجه درخواست های ورودی بین این فرآيند ها توزیع می شود و توان پردازشی به شکل موثرتری در اختیار پروژه قرار می گیرد!

استفاده از Cluster:

const cluster = require('cluster');
const os = require('os');

if (cluster.isPrimary) {
  const cpuCount = os.cpus().length;

  for (let i = 0; i < cpuCount; i++) {
    cluster.fork();
  }
}

در این مثال، برنامه به تعداد هسته‌ های CPU فرآیند جدید ایجاد می‌ کند و هر فرآیند بخشی از درخواست‌ های ورودی را پردازش خواهد کرد.

مزایای Cluster Mode:

استفاده از تمام هسته‌ های پردازنده

افزایش ظرفیت پردازش همزمان درخواست‌ ها

بهبود مقیاس‌ پذیری برنامه

افزایش پایداری سیستم در زمان ترافیک بالا

جلوگیری از بلااستفاده ماندن منابع سخت‌ افزاری

در بسیاری از پروژه‌ های Production، فعال‌سازی Cluster Mode می‌ تواند بدون اینکه تغییری در منطق برنامه داده شود افزایش قابل توجهی در توان پردازشی ایجاد کند.

8- استفاده از PM2:

مدیریت صحیح فرآیند ها و پردازش های Nodejs وقتی پروژه در محیط پروداکشن قرار می گیرد یکی از مهم ترین عوامل حفظ پایداری و عملکرد سیستم می باشد!

اجرای مستقیم برنامه با دستور node app.js برای توسعه و تست کاملا مناسب است، اما در محیط واقعی مشکلاتی مانند Crash شدن برنامه، نیاز به راه‌ اندازی مجدد، مدیریت چندین فرآیند و مانیتورینگ منابع سرور وجود دارد که مدیریت دستی بسیار دشوار هست!

PM2 به عنوان یکی از قدرتمند ترین Process Manager در نود جی اس شناخته می شود که برای اجرای برنامه در محیط های پروداکشن بسیار مناسب هست! این ابزار امکانات زیادی را در اختیار توسعه دهنده قرار می دهد و مدیریت و مانتیتورینگ پروژه را بسیار ساده تر می کند.

یکی از مهم‌ ترین قابلیت‌ های PM2، راه‌ اندازی خودکار برنامه پس از Crash شدن است. در حالت عادی اگر فرآیند Node.js متوقف شود، سرویس از دسترس خارج خواهد شد اما PM2 به صورت خودکار فرآیند را مجددا اجرا می‌ کند و از قطعی سرویس جلوگیری می‌ کند. علاوه بر این، PM2 به راحتی می‌ تواند برنامه را در حالت Cluster اجرا کند تا از تمام هسته‌ های پردازنده استفاده شود.

نصب:

npm install -g pm2 

اجرا:

pm2 start app.js -i max

توضیح دستور:

pm2 : اجرای ابزار PM2

start : شروع برنامه

app.js : فایل اصلی پروژه

-i max : اجرای برنامه روی تمام هسته‌های CPU و فعال‌سازی Cluster Mode

مزایا استفاده از PM2:

Restart خودکار در صورت Crash شدن برنامه

Load Balancing بین فرآیند ها

مانیتورینگ مصرف CPU و حافظه

مدیریت ساده Cluster Mode

مدیریت لاگ‌ ها و خطا ها

اجرای خودکار برنامه پس از ریبوت شدن سرور

در کنار بهینه‌ سازی کد ها، استفاده از یک Process Manager حرفه‌ای مانند PM2 یکی از اقدامات ضروری برای استقرار و نگهداری پروژه‌ های Nodejs محسوب می‌ شود.

9- استفاده از Worker Threads:

همانطور که گفته شد نود جی اس برای پردازش درخواست های همزمان از Event Loop استفاده می کند و همین موضوع باعث می شود سرعت بسیار بالایی در عملیاتی هایی داشته باشد که برپایه I/O می باشد!

با این وجود زمانی که برنامه با پردازش های سنگین و CPU محور مواجه می شود شرایط کاملا فرق می کند! اگر این نوع عملیات روی Thread اصلی اجرا شود Event Loop برای مدتی مسدود می شود و نمی تواند به سایر درخواست های کاربران پاسخ دهد و منتظر پایان پردازش می میاند!

این مشکل در پروژه هایی که علاوه بر مدیریت درخواست ها، وظایفی مانند پردازش فایل ، رمزنگاری و تولید گزارش های سنگین و یا محاسبات پیچیده دارند بیشتر مشاهده می شود و در چنین شرایطی حتی یک پردازش طولانی هم می تواند باعث افزایش زمان پاسخگویی تمام کاربران سیستم شود و عملکرد کلی برنامه تحت تاثیر قرار بگیرد.

برای حل این مشکل، Nodejs قابلیت Worker Threads را ارائه کرده است. این ویژگی امکان اجرای پردازش‌ های سنگین را در Thread های جداگانه فراهم می‌ کند تا Thread اصلی همچنان آزاد بماند و بتواند درخواست‌ های ورودی را بدون وقفه مدیریت کند.

در واقع Worker Threads به شما اجازه می‌ دهد بخشی از بار پردازشی برنامه را از Event Loop جدا کرده و روی Thread های مستقل اجرا کنید.

نمونه‌ هایی از پردازش‌ های CPU محور:

پردازش و ویرایش تصاویر

عملیات رمزنگاری و هش‌ گذاری

تولید فایل‌ های PDF

پردازش مدل‌ های هوش مصنوعی

فشرده‌ سازی فایل‌ ها

پردازش فایل‌ های حجیم

محاسبات ریاضی پیچیده

برای این موارد می‌توان از Worker Threads استفاده کرد.

const {
  Worker
} = require('worker_threads');

مزیت اصلی این است که Thread اصلی کاملا آزاد باقی می ماند و درخواست های دیگر منتظر پاسخ نمی مانند و صف تشکیل نمی شود!

البته Worker Threads برای تمام پروژه ها مناسب نیستند. ایجاد Thread جدید هزینه پردازشی و مصرف حافظه مخصوص به خود را دارد بنابراین بهتر است فقط برای عملیات‌ های CPU محور و زمان‌ بر از آن استفاده شود. برای عملیات‌های معمولی مانند Query های دیتابیس، درخواست‌ های HTTP یا خواندن فایل‌ ها، همان مکانیزم غیرهمزمان Nodejs معمولا بهترین و بهینه‌ ترین انتخاب خواهد بود.

10- کاهش حجم JSON Response:

خیلی وقت ها هم با بررسی مشخص می شود که مشکل کندی API در واقع نه از سمت سرور هست و نه به دیتابیس مربوط می شود! بلکه به حجم داده هایی مربوط می شود که در پاسخ برای کلاینت ها ارسال می شود!

هرچه حجم JSON بزرگ تر باشد زمان بیشتری برای تولید پاسخ، تبدیل داده ها به فرمت JSON و انتقال آن ها از طریق شبکه نیاز خواهد بود! اگر API پرترافیک باشد و کاربران زیادی همزمان به آن درخواست می زنند این موضوع می تواند کلا محسوس حس شود و تاثیر قابل توجهی بر عملکرد سیستم داشته باشد!

یکی از اشتباهات رایج این است که کل آبجکت یا سند دریافتی از دیتابیس بدون هیچ فیلتر یا پردازش اضافی برای کاربر ارسال می‌ شود. در حالی که در بسیاری از موارد تنها چند فیلد محدود برای نمایش در رابط کاربری مورد نیاز است و ارسال سایر اطلاعات صرفا باعث افزایش حجم پاسخ خواهد شد.

علاوه بر بحث عملکرد، ارسال اطلاعات غیرضروری می‌ تواند از نظر امنیت و حفظ حریم داده‌ ها نیز مشکل‌ ساز باشد. برای مثال ممکن است فیلد هایی مانند تنظیمات داخلی، اطلاعات سیستمی یا داده‌ هایی که نباید در اختیار کاربر قرار گیرند، به اشتباه در پاسخ API ارسال شوند. به همین دلیل بهتر است همیشه پاسخ‌ ها را به صورت هدفمند و فقط با فیلدهای مورد نیاز تولید کنیم.

اشتباه:

res.json(user);

اگر شیء user شامل ده‌ ها فیلد باشد، تمام اطلاعات برای کلاینت ارسال خواهد شد حتی فیلد هایی که هیچ استفاده‌ ای در سمت کاربر ندارند.

بهتر:

res.json({
  id: user.id,
  name: user.name
});

11- جلوگیری از Memory Leak:

یکی از موارد خطرناکی که به مرور می تواند زمان عملکرد یک برنامه نود جی اس را تحت تاثیر قرار دهد Memory Leak می باشد! این مشکل زمانی رخ می دهد که آرایه ها و داده هایی که دیگر مورد نیاز برنامه نیستند همچنان در حافظه باقی می مانند و امکان آزاد سازی آن ها توسط Garbage Collector فراهم نشود! در نتیجه با گذشت زمان مصرف حافظه به صورت مداوم افزایش پیدا می کند و منابع سرور به تدریج اشغال می شود!

برخلاف بسیاری از مشکلات عملکردی که از همان ابتدا قابل مشاهده هستند، Memory Leak معمولا به صورت تدریجی ظاهر می‌ شود.

ممکن است برنامه در ساعات یا حتی روز های ابتدایی بدون هیچ مشکلی کار کند، اما پس از مدتی مصرف RAM به طور غیرعادی افزایش پیدا کند و عملکرد سیستم کاهش یابد. به همین دلیل شناسایی این مشکل معمولا دشوارتر از سایر مشکلاتی هست که در عمل رخ می دهد.

اگر Memory Leak برای مدت طولانی برطرف نشود، می‌ تواند باعث افزایش زمان اجرای Garbage Collector، کاهش سرعت پاسخ‌ گویی API ها و حتی Crash شدن کامل فرآیند Nodejs شود. به همین دلیل مانیتورینگ مصرف حافظه و بررسی دوره‌ ای وضعیت Heap یکی از اقدامات ضروری در پروژه‌ های Production محسوب می‌ شود.

Memory Leak یکی از خطرناک‌ ترین مشکلات Nodejs است.

نشانه‌ ها:

افزایش مداوم مصرف RAM

کند شدن تدریجی برنامه

افزایش زمان پاسخ‌ گویی API ها

افزایش فعالیت Garbage Collector

Crash شدن فرآیند Nodejs

مثال رایج:

const users = [];

app.get('/', async () => {
  users.push(await getUser());
});

در این مثال، با هر درخواست جدید یک داده به آرایه users اضافه می‌ شود اما هیچ‌گاه از حافظه حذف نمی‌ شود. در نتیجه اندازه آرایه به مرور بزرگ‌ تر شده و مصرف حافظه دائما افزایش پیدا می‌ کند.

دلایل رایج Memory Leak در Nodejs:

نگهداری داده‌ های غیرضروری در آرایه‌ ها و Object ها

استفاده نادرست از Cache بدون تعیین محدودیت

Event Listener هایی که حذف نمی‌ شوند

Timer ها و Interval های رها شده

نگهداری Reference به اشیائی که دیگر مورد نیاز نیستند!

برای جلوگیری از Memory Leak باید ساختار داده‌ ها، Cache ها و منابع برنامه به صورت دوره‌ای بررسی شوند و اطمینان حاصل شود که داده‌ های بلااستفاده از حافظه حذف می‌ شوند. هرچه زودتر این مشکل شناسایی شود، هزینه رفع آن در محیط Production کمتر خواهد بود و از بروز اختلالات جدی در عملکرد سیستم جلوگیری می‌ شود.

 

12- بهینه‌ سازی لاگ‌ گیری:

ثبت لاگ یکی از بخش های ضروری هر پروژه می باشد و نقش بسیار مهمی در مانیتورینگ و تحلیل رفتار سیستم دارد! بدون لاگ گیری در اکثر مواقع پیدا کردن مشکل بسیار دشوار می باشد و باید کدها از ابتدا بررسی شود!

با این وجود لاگ گیری بیش از حد و ثبت اطلاعات غیرضروری می تواند خود به یکی از عوامل کاهش عملکرد برنامه تبدیل شود! هر بار که اطلاعاتی در کنسول یا فایل های لاگ نوشته می شود بخشی از منابع سیستم برای پردازش و ذخیره آن مصرف می شود.

در پروژه هایی که ترافیک بالایی دارند برای ثبت لاگ برای هر درخواست یا هر Query یا هر عملیات کوچکی می تواند حجم بسیار زیادی تولید کند این موضوع علاوه بر افزایش مصرف CPU و حافظه باعث رشد سریع فایل های لاگ و حتی دشوار تر شدن بررسی و تحیل لاگ ها نیز می شود!

به همین دلیل باید بین میزان اطلاعات مورد نیاز برای مانیتورینگ و هزینه‌ ای که لاگ‌ گیری به سیستم تحمیل می‌ کند تعادل برقرار شود.

یکی از اشتباهات رایج در پروژه‌ های Nodejs، استفاده گسترده از console.log() در محیط Production است. اگرچه این روش برای توسعه و دیباگ کردن بسیار کاربردی است، اما در سیستم‌ های واقعی و پرترافیک می‌ تواند باعث افت عملکرد شود. به همین دلیل استفاده از Logger های حرفه‌ ای و بهینه توصیه می‌ شود. هرچقدر در محیط های پروداکشن ذخیره لاگ کمتر اتفاق بیفتد افزایش سرعت NodeJS بهتر اتفاق می افتد.

اشتباه:

console.log(user);

اگر این کد برای هر درخواست اجرا شود، در ترافیک بالا حجم زیادی از عملیات I/O ایجاد خواهد کرد.

بهتر:

استفاده از Pino

const pino = require('pino');

const logger = pino();

مزایا:

سرعت بسیار بالا

ساختارمند بودن لاگ‌ ها

مصرف کمتر منابع

مناسب برای سیستم‌ های پرترافیک

قابلیت یکپارچه‌ سازی با ابزارهای مانیتورینگ

مدیریت بهتر خطاها و رویدادها

علاوه بر انتخاب ابزار مناسب، بهتر است سطح لاگ‌ ها نیز مدیریت شود. برای مثال در محیط Production معمولا تنها لاگ‌ های مهم مانند Error ها، Warning ها و رویدادهای حیاتی ثبت می‌ شوند، در حالی که لاگ‌ های Debug صرفا در محیط توسعه فعال هستند. این رویکرد باعث می‌ شود علاوه بر حفظ اطلاعات مهم، از ایجاد بار اضافی روی سرور نیز جلوگیری شود.

13. استفاده از Connection Pool:

برقراری ارتباط با دیتابیس یکی از پر هزینه ترین عملیات ها در بسیاری از برنامه های سمت سرور می باشد هر بار که یک اتصال جدید به دیتابیس ایجاد می شود فرآيند هایی مانند احراز هویت و تخصص منابع انجام می شود که هم زمان بر هست و هم منابع سیستم را مصرف می کند! اگر برای هر درخواست یک کانکشن جدید ایجاد و سپس بسته شود با گذشت زمان عملکرد برنامه تحت تاثیر قرار می گیرد!

وقتی به این موضوع فکر کنیم که در پروژه های پرترافیک روزانه صد ها و هزاران درخواست به صورت همزمان به سرور ارسال می شوند و هر کدام اگر بخواهند یک اتصال جدید به دیتابیس ایجاد کنند در چنین شرایطی نه تنها زمان پاسخگویی افزایش پیدا می کند بلکه فشار بسیار بالایی هم به دیتابیس و سرور وارد می شود.

برای حل این مشکل و افزایش سرعت NodeJS از مکانیزمی به نام Connection Pool استفاده می‌ شود. در این روش مجموعه‌ ای از Connection های آماده از قبل ایجاد شده و در یک Pool نگهداری می‌ شوند. هر زمان که برنامه به دیتابیس نیاز داشته باشد، به جای ایجاد Connection جدید از یکی از Connection های موجود در Pool استفاده می‌ کند و پس از پایان عملیات، اتصال مجددا به Pool بازگردانده می‌ شود.

این رویکرد باعث کاهش زمان انتظار برای برقراری ارتباط با دیتابیس و استفاده بهینه‌ تر از منابع سیستم خواهد شد.

مزایای Connection Pool:

کاهش زمان برقراری اتصال به دیتابیس

افزایش سرعت اجرای Query ها

کاهش مصرف منابع سرور

مدیریت بهتر تعداد Connection های همزمان

افزایش پایداری سیستم در زمان ترافیک بالا

جلوگیری از ایجاد تعداد بیش از حد Connection

در MongoDB، PostgreSQL، MySQL و اکثر دیتابیس‌ های مدرن، Connection Pool به صورت پیش‌ فرض یا از طریق تنظیمات درایورهای رسمی قابل استفاده است. به همین دلیل در پروژه‌های Production توصیه می‌ شود از ایجاد و بستن مداوم Connection ها خودداری کرده و از Pool های مدیریت‌ شده برای ارتباط با دیتابیس استفاده شود.

14- حذف Dependency های غیرضروری:

یکی از مواردی که عموما دیده می شود در فرآیند توسعه نادیده گرفته می شود افزایش تعداد Dependency های پروژه می باشد!

در بسیاری از پروژه ها پکیج های مختلف برای تست و توسعه و یا بررسی قابلیت های جدید نصب می شوند و بعد از مدتی دیگر برخی از آن ها مورد استفاده قرار نمی گیرند و همچنان در پروژه باقی می مانند. ممکن است این پکیج های بی استفاده علاوه بر اینکه باعث شلوغ کردن ساختار پروژه می شوند عملکرد و امنیت سیستم را نیز تحت تاثیر قرار دهند.

هر پکیجی که به سیستم اضافه می شود طبیعی هست که بخشی از منابع سیستم را مصرف می کند و برخی از کتابخانه ها در زمان Startup بارگذاری می شوند و برخی دیگر حافظه را اشغال می کنند و برخی نیز وابستگی های متعددی به همراه خود وارد پروژه می کنند. هرچه تعداد این پکیج‌ ها بیشتر باشد، زمان راه‌ اندازی برنامه و حجم کلی پروژه نیز افزایش پیدا خواهد کرد.

علاوه بر بحث عملکرد، وابستگی‌ های غیرضروری می‌ توانند ریسک‌ های امنیتی نیز ایجاد کنند. هر پکیج نصب‌ شده به معنای اضافه شدن کد های شخص ثالث به پروژه است و در صورت وجود آسیب‌ پذیری امنیتی در آن کتابخانه، سطح حمله سیستم بزرگ‌ تر خواهد شد. به همین دلیل بازبینی دوره‌ ای Dependency ها یکی از اقدامات مهم در نگهداری پروژه‌ های Production محسوب می‌ شود.

هر پکیج npm:

حافظه مصرف می‌ کند.

زمان Startup برنامه را افزایش می‌ دهد.

حجم پروژه را بیشتر می‌ کند.

سطح حمله امنیتی را بزرگ‌تر می‌ کند.

فرآیند نگهداری و بروزرسانی پروژه را پیچیده‌ تر می‌ کند.

به صورت دوره‌ ای پروژه را مورد بررسی قرار دهید و پکیج‌ های بلااستفاده را حذف کنید تا هم بخشی از حافظه را آزاد کنید و هم عملکرد سیستم را بهبود ببخشید و هم از نظر امنیتی پروژه ارتقا پیدا کند.

بررسی:

npm prune

این دستور پکیج‌ هایی را که در package.json تعریف نشدند اما همچنان در پوشه node_modules وجود دارند، حذف می‌ کند.

همچنین بهتر است در بازه‌ های زمانی مشخص Dependency های پروژه را بررسی کرده و کتابخانه‌ هایی که دیگر مورد استفاده قرار نمی‌ گیرند حذف شوند.

هرچه پروژه سبک‌ تر و وابستگی‌ های آن کمتر باشند افزایش سرعت NodeJS بیشتر به چشم می آید و  فرآیند استقرار، نگهداری، بروزرسانی و عیب‌ یابی نیز ساده‌ تر خواهد بود. در بسیاری از موارد حتی می‌ توان یک پکیج سنگین را با چند خط کد ساده جایگزین کرد و مصرف منابع برنامه را کاهش داد.

15- استفاده از Streaming:

مدیریت فایل‌ های حجیم یکی از چالش‌ های رایج در پروژه‌ های نود جی اس است. بسیاری از توسعه‌ دهندگان در زمان کار با فایل‌ ها از روش‌ هایی استفاده می‌ کنند که کل محتویات فایل را یکجا در حافظه بارگذاری می‌ کند. این رویکرد برای فایل‌ های کوچک مشکلی ایجاد نمی‌ کند، اما زمانی که حجم فایل‌ ها به چند صد مگابایت یا چند گیگابایت برسد، مصرف حافظه به شدت افزایش پیدا کرده و عملکرد برنامه تحت تاثیر قرار می‌ گیرد.

در چنین شرایطی، بارگذاری کامل فایل در حافظه می‌ تواند باعث افزایش مصرف RAM، کند شدن برنامه و حتی Crash شدن فرآیند نود جی اس شود. این مشکل در سرور هایی که همزمان چندین فایل بزرگ را پردازش می‌ کنند اهمیت بیشتری پیدا می‌ کند، زیرا هر فایل بخش قابل توجهی از حافظه را اشغال خواهد کرد.

برای حل این مشکل، و افزایش سرعت NodeJS  خود این پلتفرم مکانیزمی به نام Streaming ارائه می‌ دهد. در این روش داده‌ ها به جای اینکه به صورت یکجا در حافظه بارگذاری شوند، به صورت بخش‌های کوچک یا به اصطلاح Chunk خوانده یا ارسال می‌ شوند. این موضوع باعث می‌ شود مصرف حافظه تقریبا ثابت باقی بماند و برنامه بتواند فایل‌ های بسیار بزرگ را نیز با کارایی بالا مدیریت کند.

برای فایل‌ های بزرگ:

اشتباه:

const data = fs.readFileSync(file);

در این حالت کل فایل قبل از پردازش وارد حافظه می‌ شود که برای فایل‌ های حجیم بسیار پر هزینه است.

بهتر:

fs.createReadStream(file);

در این روش فایل به صورت تدریجی و بخش‌ بخش خوانده می‌ شود و نیازی به بارگذاری کامل آن در حافظه وجود ندارد.

مزایای Streaming:

کاهش چشمگیر مصرف حافظه

افزایش سرعت پردازش فایل‌ های بزرگ

جلوگیری از Crash شدن برنامه در فایل‌ های حجیم

امکان پردازش همزمان فایل‌ های بزرگ متعدد

مناسب برای دانلود و آپلود فایل‌ ها

بهبود عملکرد کلی سرور

Streaming تنها برای خواندن فایل‌ ها کاربرد ندارد و در انتقال داده از طریق شبکه، استریم ویدئو، پردازش فایل‌ های لاگ، ارتباط با سرویس‌ های خارجی و بسیاری از سناریو های دیگر نیز استفاده می‌ شود.

16- استفاده از CDN:

یکی دیگر از اقدامات برای افزایش سرعت NodeJS استفاده از CDN می باشد!

در خیلی از وب سایت ها بخش قابل توجهی از ترافیک مصرفی مربوط به فایل های استاتیک مانند تصاویر، فایل‌ های CSS، فایل‌ های JavaScript، فونت‌ ها و سایر منابعی است که به دفعات توسط کاربران دانلود می‌ شوند.

اگر تمام این فایل ها مستقیم از سرور اصلی دانلود شوند با افزایش تعداد کاربران فشار زیادی به سرور وارد می شود و ممکن است زمان بارگذاری صفحات هم افزایش پیدا کند.

یکی از بهترین راهکار ها برای حل این مشکل این است که از CDN استفاده کنید! به کمک CDN شما می توانید نسخه ای از فایل های استاتیک خود را در سرور های مختلف توزیع کنید تا زمانی که کاربر به سایت شما درخواست ارسال می کند محتوا از نزدیک ترین سرور CDN به موقعیت جغرافیایی او تحویل داده شود و دیگر نیازی نیست درخواست مستقیم وارد سرور اصلی شود!

این کار علاوه بر کاهش ترافیک روی سرور اصلی باعث افزایش سرعت بارگذاری صفحات هم می شود به همین دلیل است که بسیاری از سایت های بزرگ از CDN برای ارائه فایل های استاتیک خود استفاده می کنند.

مزایا:

کاهش بار سرور اصلی

تحویل سریع‌ تر محتوا به کاربران

استفاده از Cache در سطح جهانی

کاهش زمان بارگذاری صفحات

کاهش مصرف پهنای باند سرور

افزایش پایداری و دسترس‌ پذیری سرویس

بهبود تجربه کاربری در مناطق مختلف جغرافیایی

استفاده از CDN به‌ خصوص برای پروژه‌ هایی که کاربران آن‌ ها از شهرها یا کشورهای مختلف به سرویس دسترسی دارند اهمیت زیادی دارد. به همین دلیل CDN یکی از مهم‌ ترین اجزای زیرساخت در سیستم‌ های مدرن و مقیاس‌ پذیر محسوب می‌ شود.

17- مانیتورینگ عملکرد:

برای اینکه بهینه سازی های انجام شده بررسی شود که تا چه اندازه موفق بوده است حتما باید اندازه گیری های لازم صورت بگیرد.

همانطور که در قسمت های مختلف گفته شده است بسیاری از مشکلات نود جی اس به مرور زمان ایجاد می شود و تا زمانی که داده های دقیقی از وضعیت سیستم در اختیار نداشته باشیم شناسایی علت اینکه چرا پروژه دچار کندی یا افت عملکرد شده است ساده نخواهد بود! به همین دلیل مانیتورینگ کردن یکی از مهم ترین بخش های نگهداری و بهینه سازی هر پروژه پروداکشنی محسوب می شود.

خیلی از توسعه دهندگان تنها زمانی به سراغ بررسی عملکرد سیستم می روند که کاربران آن ها از کندی های پیش آمده شاکی شده اند یا سرور دچار مشکل شده است! در حالی که یک سیستم مانیتورینگ مناسب می تواند قبل از تبدیل شدن یک مشکل کوچک به یک بحران بزرگ نشانه های آن را شناسایی کند و تیم توسعه با وارد عمل شدن موضوع را حل کند تا سیستم به طور کامل پایدار باشد.

مانیتورینگ به شما کمک می‌ کند رفتار برنامه، مصرف منابع و وضعیت زیرساخت را به صورت لحظه‌ ای بررسی کنید. با تحلیل این اطلاعات می‌ توان مشکلات احتمالی، Memory Leak ها، افزایش غیرعادی مصرف CPU، کند شدن Query های دیتابیس و بسیاری از مشکلات دیگر را سریع‌ تر شناسایی و برطرف کرد.

بدون مانیتورینگ نمی‌توان مشکلات را پیدا کرد یا دستکم پیدا کردن مشکلات زمان بر خواهد بود و هرچه قدر داون تایم و کندی بیشتر باشد ضربه ای که به اعتبار برند و پروژه زده می شود بالاتر خواهد بود.

ابزارهای محبوب مانیتورینگ:

PM2

Grafana

Prometheus

New Relic

Datadog

شاخص‌های مهم:

CPU Usage

Memory Usage

Event Loop Delay

Response Time

Throughput

علاوه بر این شاخص‌ ها، بررسی تعداد درخواست‌ های همزمان، نرخ خطا ها، وضعیت Connection های دیتابیس و مدت زمان اجرای Query ها نیز می‌ تواند دید بسیار دقیقی از سلامت سیستم در اختیار شما قرار دهد.

هرچه داده‌های مانیتورینگ کامل‌ تر باشند، تصمیم‌ گیری برای بهینه‌ سازی و افزایش مقیاس‌ پذیری پروژه نیز آسان‌ تر خواهد بود.

در نهایت باید توجه داشت که مانیتورینگ یک فعالیت مقطعی نیست، بلکه بخشی دائمی از چرخه نگهداری نرم‌ افزار است. حتی بهترین معماری‌ ها و بهینه‌ ترین کدها نیز بدون نظارت مداوم ممکن است در طول زمان با مشکلات مواجه شوند. به همین دلیل استفاده از ابزار های مانیتورینگ حرفه‌ ای یکی از الزامات اصلی مدیریت پروژه‌ های Nodejs در محیط Production به شمار می‌ رود.

سوالات متداول:

در ادامه به چند سوال متداول پاسخ می دهیم که ممکن است برای شما پیش آمده باشد.

آیا Nodejs برای پروژه‌ های بزرگ مناسب است؟

بله کاملا مناسب می باشد البته این بستگی به معماری پروژه هم دارد ولی شرکت های بزرگی در دنیا برای بخش هایی که نیاز به سرعت پاسخگویی دارند از نود جی اس استفاده می کنند و این پلتفرم کاملا برای پروژه های بزرگ و پر ترافیک مناسب می باشد.

Redis چقدر می‌ تواند سرعت را افزایش دهد؟

استفاده از کش علاوه بر اینکه باعث بهبود عملکرد سیستم و کاهش مصرف منابع می شود می تواند سرعن سیستم را هم به شکل قابل ملاحظه ای افزایش دهد!

بسته به نوع داده و حجم درخواست‌ ها، استفاده از Redis می‌ تواند زمان پاسخ API را از چند صد میلی‌ ثانیه به چند میلی‌ ثانیه کاهش دهد.

چه زمانی باید از Worker Threads استفاده کنیم؟

وقتی یک کار سنگین مثل پردازش فایل یا محاسبات زیاد داریم که باعث کند شدن سرور می‌ شود، از Worker Threads استفاده می‌ کنیم تا فشار از روی سرور اصلی برداشته شود.

مهم‌ترین عامل کندی Nodejs چیست؟

در بیشتر مواقع کندی از خود Nodejs نیست، بلکه از دیتابیس یا کد های بهینه‌ نشده می‌ آید. مثلا کوئری‌ های سنگین یا نبود کش می‌ تواند سرعت را پایین بیاورد.

جمع‌ بندی:

بهینه سازی در پروژه های نود جی اس امری ضروری می باشد که هر توسعه دهنده در پروژه خود باید آن را مدنظر داشته باشد! با بهینه سازی می توان افزایش سرعت NodeJS را به حد قابل قبولی افزایش داد.

با اینکه نود جی اس یک پلتفرم کاملا سریع می باشد اما در پروژه ای به مرور زمان و افزایش کاربر و ترافیک دچار کندی خواهد شد و بهینه سازی نقش بسزایی در جلوگیری از افت عملکرد سیستم و قطعی های احتمالی خواهد داشت.

بهینه‌ سازی در Nodejs فقط مجموعه‌ ای از اقداماتی مثل Cache یا کاهش مصرف حافظه نیست بلکه یک نگاه سیستمی به نحوه طراحی، اجرا و نگهداری یک اپلیکیشن است. در عمل، بیشتر مشکلات عملکرد پروژه ها زمانی ظاهر می‌ شوند که پروژه از مرحله اولیه عبور می‌ کند و وارد دنیای واقعی با کاربران زیاد، داده‌ های حجیم و فشار های غیرقابل پیش‌بینی می‌ شود.

وقتی یک سیستم زیر بار فشار سنگین ترافیکی قرار می گیرد این بهینه سازی ها هستند که تعیین می کنند کدام سیستم به شکل پایدار به کار خود ادامه دهد و کدام سیستم زیر بار فشار های سنگین دچار کندی و قطعی های طولانی مدت شود.

بهینه سازی صرفا به این جهت نیست که کمک کند پروژه فقط در دسترس باشد و کار کند یا باعث افزایش سرعت NodeJS شود! بلکه در مقیاس بالا هم پروژه قابل اعتماد می ماند یعنی همزمان می تواند تعداد زیادی درخواست را بدون افت کیفیت پاسخ دهد!

در نهایت باید بدانید که بهینه سازی عملکرد سیستم را به شکل چشم گیری افزایش می دهد و شما در روند توسعه بهینه سازی را باید بخشی از طراحی بدانید نه یک قابلیت اضافه که بعدا هم امکان اضافه شدن خواهد داشت!

هر تصمیمی که در سطح کد یا دیتابیس می گیرید در مقیاس بالا می تواند اثر چند برابری داشته باشد برای همین پروژه هایی موفق هستند که در همان قدم اول در توسعه به زمان هایی فکر می شود که با ترافیک های بسیار بالا مواجه خواهد شد و مدیریت مصرف منابع در این شرایط یک ضرورت محسوب می شود.

Rate this post