NodeJS چیست؟

برای اجرای جاوااسکریپت در مرورگر نیاز به یک موتور جاوااسکریپت هست تا کد جاوااسکریپت را به یک کد قابل فهم برای کامپیوتر تبدیل کند، برای مثال برای اجرای جاوااسکریپت در مرورگر Microsoft edge از موتور Chakra در فایرفاکس از Spider Monkey و در مرورگر کروم از موتور V8 استفاده می کند، تا قبل از 2009 جاوااسکریپت فقط در مرورگر اجرا می شد در سال 2009 Ryan Dahl نود را معرفی کرد که ایده درخشانی برای اجرای جاوااسکریپت را در خارج از مرورگر است.

NodeJS چگونه کار می کند؟

برای اجرای یک پروژه NodeJS کد جاوااسکریپت می نویسیم و این کد را با node اجرا می کنیم. حالا می خواهیم درمورد نحوه کار NodeJS بیشتر بدانیم.

Nodejs برای اجرا از موتورV8 و کتابخانه libuv استفاده می کند، V8 یک موتور متن باز هست که توسط گوگل ایجاد شده است، بر همین اساس می توانیم کد جاوااسکریپت را خارج از مرورگر و در ترمینال اجرا کنیم.

Libuv یک پروژه متن باز C++ است که به Node اجازه دسترسی به سیستم عامل و فایل های سیستمی را می دهد.

چرا از Node استفاده می کنیم، چرا به طور مستقیم از V8 و libuv استفاده نمی کنیم؟

V8 و libuv براساس کد جاوااسکریپت پیاده سازی نشده اند، طبیعتا یک برنامه نویس جاوااسکریپت هم نمی خواهد با کد C++ درگیر شود، Node یک اینترفیس خوب در اختیار برنامه نویس ها قرار می دهد تا فقط کد جاوااسکریپت خود را بنویسند و آن به راحتی توسط Node اجرا شود. Node از دو بخش جاوااسکریپت و C++ تشکیل شده است.

میزان استفاده از کد جاوااسکریپت و C++ در هر قسمت در شکل پایین مشخص شده است.

کار دیگری که Node انجام می دهد یکسری wrapper و API های سازگاری را فراهم می آورد تا در پروژه استفاده کنیم. Node یکسری ماژول های استانداردی مثل http crypto, و path دارد که اینها بصورت توابع در libuv تعریف شده اند.



چطور توابع و ماژول ها در Node تعریف می شوند؟

اگر به سورس کد Node نگاه کنیم یک فولدرlib وجود دارد که تمام توابع و ماژول های جاوااسکریپتی که در پروژه ها استفاده می کنیم در این فایل قرار دارند و در فولدر src پیاده سازی سمت C++ از این توابع قرار دارد.

برای درک بهتر چگونگی ارتباط و اجرای نود باید بدانیم که Node شامل دو بخش است یک سمت جاوااسکریپت Node است و سمت دیگر بخش C++ هست که برای اتصال جاوااسکریپت به C++ از binding(Name of function) استفاده می شود و توسط V8 مقادیر جاوااسکریپت به C++ ترجمه می شوند، مثل تعریف آبجکت ها و آرایه ها و boolean.




Event loop چیست و چگونه کار می کند؟

برای مدیریت کردن کدهای ناهمگام (Async) در Node از event loop استفاده می شود.

وقتی یک برنامه ای را در کامپیوتر اجرا می کنیم یک فرآیند(process) را شروع می کنیم، فرآیند یک موجودیت از یک برنامه کامپیوتری است . Threadها واحدهای عملیاتی هستند در حقیقت یک thread لیستی از تعدادی عملیات است که باید در cpu اجرا شود یک thread به cpu داده می شود و باید همه عملیات ها از بالا به پایین اجرا شوند. یک فرآیند می تواند چندین thread داشته باشد.


scheduling توانایی تصمیم گیری در مورد این است که کدام فرآیند در هر زمان اجرا شود. Scheduling توسط سیستم عامل کنترل می شود، کامپیوتر تعداد محدودی منابع دارد و cpu می تواند عملیات زیادی را در هر ثانیه انجام دهد هدف این است که threadها درگیر یک عملیات نمانند برای بهبود استفاده از thread ها دو راهکار وجود دارد.


میتوانیم cpu coreهای بیشتری را اضافه کنیم در این صورت چندین thread در یک زمان واحد می توانیم اجرا کنیم.

روش دوم اینکه زمان هایی که یک thread متوقف می شود شناسایی شود و در آن زمان thread آزاد شود و عملیات دیگری را اجرا کند.

Node یک thread ایجاد می کند و همه کدها را داخل یک thread اجرا می کند. داخل یک single thread یک event loop قرار دارد.


event loop مثل یک ساختار کنترلی هست که تصمیم می گیرد کدام عملیات اجرا شود، event loop یک core از هر برنامه ای هست که در حال اجرا هست، می توانیم بگوییم event loop مثل یه حلقه while عمل می کند که برای ادامه اجرا یا اتمام فرآیند یکسری شرایط را بررسی می کند.

Function shouldContinue(){

Check #1 : any setTimeout, setInterval

Check #2: any pending os task such as httpRequest

Check #3: any pending long running operation like fs module

}
While(shouldcontinue()){
}

Check #1: اگر کد ناهمگام مثل setTimeout, setInterval وجود داشته باشد event loop در حال اجرا باقی می ماند تا پس از زمان مشخص شده callback مخصوص به آن صدا زده شود.

Check #2: یکسری از ماژول ها مثل ایجاد و دریافت درخواست های http و یا دریافت فایل ها جزو این دسته قرار می گیرند.

Check#3: در event loop چک می شود که توابعی در داخل thread poolها در حال اجرا هستند یا نه.

آیا نود single thread است؟

Event loop در نود بصورت کامل single thread است. وقتی ما یک برنامه را اجرا می کنیم یک single thread از event loop ایجاد می شود. در اینجا این سوال مطرح می شود اگر سیستم ما چندین cpu داشته باشد NodeJS نمی تواند از آنها استفاده کند در نتیجه برنامه خیلی سریع اجرا نمی شود؟

برای پاسخ به این سوال باید گفت که برخی از توابع و کتابخانه های Node در داخل thread pool اجرا می شوند و برخی دیگر از توابع برای اجرا از ویژگی های async os استفاده می کنند، این توابع single thread نیستند.

برخی کتابخانه ها و فریم ورک هایی که در Node قرار دارند در حقیقت single thread نیستند برای مثال الگوریتم رمزگذاری داده PBKDF2 را در نظر بگیرید که داخل کتابخانه crypto هست، در این الگوریتم بیشتر کارها در سمت C++ از Node انجام می شود، Node در سمت C++ و libuv تصمیم می گیرند که محاسبات بصورت کامل در خارج از event loop انجام شوند که برای اجرا یک thread pool را صدا می زنند این thread pool بصورت پیش فرض از چهار thread تشکیل شده اند که می توانند برای اجرای کارهای محاسباتی مثل PBKDF2 استفاده شوند.


برای مثال اگر ما پنج تسک داشته باشیم و thread pool دارای چهار thread باشد در این صورت چهار تسک بصورت همزمان اجرا می شوند و تسک آخر با کمی تاخیر. بسیاری از توابع استاندارد از این thread pool استفاده می کنند در نتیجه Node برای محاسبات single thread نیست.


برای تغییر تعداد thread های thread pool می توان مقدار تعیین شده در env را تغییر داد.

Process.env.UV_THREADPOOL_SIZE = 2

اینکه چه توابعی در nodejs از thread pool استفاده می کنند بستگی به سیستم عامل ما دارد ولی اصولا در همه سیستم عامل های توابع fs و crypto از thread pool استفاده می کنند.

در حقیقت تسک هایی که در thread pool در حال اجرا هستند جزو check#3 در event loop قرار می گیرند و در صورتی که اجرای تسک مربوطه تمام شود callback مربوط به آن صدا می شود.

پس همانطور که گفته شد یکسری از توابع و کتابخانه های Node در داخل thread pool اجرا می شوند ولی یکسری ازتوابع دیگر از ویژگی های async os استفاده می کنند، این توابع نماینده هایی برای سیستم عامل هستند و بصورت کامل در خارج از event loop اجرا می شوند، مثل درخواست های http همه کارها توسط خود سیستم عامل انجام می شود و برای اجرای این توابع thread pool وجود ندارد، بعنوان مثال برای ایجاد یک درخواست http کتابخانه libuv یک نماینده برای ارسال درخواست به سیستم عامل هست و پس از آن منتظر دریافت سیگنال از سیستم عامل می ماند در این صورت هیچ بلاکی در کد جاوااسکریپت ما صورت نمی گیرد و خود سیستم عامل تصمیم می گیرد که یک thread جدید ایجاد کند یا نه.


بنابراین تسک هایی که از ویژگی های async os استفاده می کنند جزو شرایط check#2 در event loop قرار می گیرند و تازمانی که یک درخواست درحال اجرا باشد حلقه داخل event loop در حال اجرا باقی می ماند.

پس از اتمام اجرا وقتی همه شرایط بررسی می شوند و callbackهای مربوطه صدا زده می شوند، عملیات داخل event loop به پایان می رسد کدهای clean up اجرا می شود تا یکسری running server ها و فایل های باز را ببندد.

[1] Stephen Grider. Udemy – Node JS: Advanced Concepts 2020-3.