Node.js Architecture
Introduction
Node.js is an open-source, cross-platform, backend JavaScript runtime environment. that runs on a V8 engine and executes JavaScript outside a web browser. It implements an event-driven non-blocking I/O modal.
Node.js components
Node.js is divided into two main components.
Its core is built in C/C++. It combines different libraries mainly Google’s V8 JavaScript runtime environment and Node’s Libuv library which is responsible for the asynchronous nature of node.js.
Figure 1.1
Node.js API/Modules
Node.js API contains all of the Javascript Application code we used to develop our application. Including node modules and other modules that we develop to use in our application. This component can also be referred as application.
Node.js Core
Node.js core contains C/C++ code. It contains different C/C++ libraries that interact with application code and make it possible to run JS on the server-side.
Google’s V8 JavaScript engine.
V8 is Google’s high-performance JavaScript engine, which is written in C++ and is used in Google Chrome. It is a just in time (JIT) compiler, compilesJavaScript directly into native machine code immediately prior to execution, ready to execute avoiding any intermediate code which is further interpreted.
It contains a call stack, heap memory, and garbage collector. The garbage collector is further divided into two categories: new space and the old space, both are located in heap and keep track of JavaScript objects as a reference. The new object is added to the new space. When new space reaches its maximum amount of size the garbage collector will move dead objects from new memory to old memory and create space for new objects. It does not allow manual memory management.
LIBUV
libuv is a multi-platform C library that provides support for asynchronous I/O based on event loops. It supports epoll , kqueue , Windows IOCP, and Solaris event ports. It is the heart of Node.js. It is responsible for the main event loop and asynchronous operations. It contains an event loop and a fixed-sized thread pool, a thread is only allocated to an I/O event. So by delegating asynchronous tasks to libuv the V8 engine and event loop are free to perform other tasks from the event queue.
Libuv also contains a networking module to perform network operations.
Asynchronous TCP & UDP sockets and read/write files all tasks are performed by libuv.
Other C/C++ Components
These two C++ libraries V8 and LIBUV are the main components of the node's core other C/C++ libraries like C-Ares, Zlib, HTTP-parser, and open SSL are used for compression, encryption, asynchronous DNS request, etc.
Bindings
As described above node.js modules are written in JavaScript and its core is written in C/C++, there two are different languages so how two different languages can interact with each other? To solve this problem bindings come into play. Binding acts as a glue code between Node.js core and Application code.it exposes C++ libraries to JavaScript.
You can think of bindings as a bridge between Node.js Core which is written in C/C++ and Node’s Application code written in JavaScript.
Recommended by LinkedIn
Motivation
There are two types of bindings
Node.js bindings are already written to glue core components with JavaScript. C/C++ add-ons is our own glue code that we write for that specific C/C++ (third party or our own C/C++) library we want to include in the node’s core.
How Node.js works.
Work Flow
Figure 1.2
First V8 Compiles JavaScript code directly into native machine code immediately prior to execution. After compilation code interacts with Node’s core and all events associated with JavaScript code are registered.
Whenever an event is triggered it is enqueued in the event queue. If the call stack is free event loop assigns that event to the call stack and blocks up till the call stack is free.
In call-stack if the operation is not asynchronous all processing will be done in the call stack. And event loop blocks up till the operation is completed.
If the code is asynchronous then the call stack assigns that task to libuv and frees itself to do processing for the next event.
When the asynchronous task is completed by libuv. It assigns that task/event back to the event queue. Event loop looking for events will again push the task to call stack and response is sent and so on.
Libuv maintains a limited size thread pool for I/O operations and DNS-related operations. If there is an I/O operation. Libuv delegates that task to a thread and that I/O operation will be carried out by that thread.
Libuv uses multiple strategies (epoll, Kqueue, timers etc.) to achieve asynchronicity depends upon the kind of event being handled
References:
Good job