The Node.js Architecture!

The Node.js Architecture!

Feb 25, 2022

Hello, everyone. I hope you are doing good! Today, in this article, I will discuss the working of awesome node.js. A deep dive into what happens as the code runs in the node runtime. It's core libraries, dependencies, and the brain of node.js.

Node.js is written in Javascript alone and in c++ and Javascript to operate correctly. There are many libraries upon which node depends. However, V8 and LIBUV are the two foremost dependencies that handle most node.js operations.

These two dependencies provide a very nice abstract layer and allow us to write pure JavaScript code that runs in Node.js and still give us access to file-reading features implemented in LIBUV and other C++ libraries behind the scenes. The beauty of all this is that Node.js binds all these libraries together, irrespective of whether they are written in C++ or JavaScript, giving us access to their functions in pure JavaScript. It makes it much easier for us to focus on the application code without dealing with the C++ code.

Let's explore them one at a time now.

V8

Node.js is built on the V8 engine of Google. It is the fastest javascript engine. The V8 engine converts the javascript code into the machine code, which the computer understands. The result is then generated and returned to node.js. Node.js cannot understand the javascript code we write without V8. Besides javascript, V8 also uses C++.

**Before Starting with LIBUV, you should have a basic understanding of Asynchronous I/O.

Asynchronous Input-Output

The Asynchronous I/O allows applications to overlap processing with I/O operations. In simple words, the goal is that the program should never block.

In synchronous I/O, the thread (a thread is just a sequence of instructions) will wait till the entire operation is finished. On the other hand, in Asynchronous I/O, the line does not wait during the operations. The operation will run in the background, and it will be called when it is finished. The Asynchronous I/O feature enables an application to have more CPU time available to perform other processing while the I/O is taking place.

LIBUV

LIBUV is another significant dependency on node.js. It gives node.js access to the machine operating system, networking, file system, and more. LIBUV is an open-source library with a strong focus on asynchronous I/O (Input-output). LIBUV is written in C++. Apart from focusing on the asynchronous I/O, LIBUV also implements two essential features: event loop and thread pool.

Event Loop handles more straightforward tasks, and the thread pool manages the heavy tasks. Let's explore them one by one.

Event Loop

When we use Node.js on a computer, there is a node process running on that computer. The process is just a program in execution. Now in that process, Node.js runs in a single thread. A thread is just a sequence of instructions, and It's not essential to deeply understand what a thread or a process is. Imagine a thread as a box where our code is executed in a computer's processor. Now, what is essential to understand here, is the fact that the node runs in just one thread. For example, if we have four different tasks, all four will happen in one single thread.

We have one new thread created for every task in languages like PHP. So, again, if you run your Node application, it will run in just a single thread. No matter if you have one user or 100 users or maybe 100 million users are accessing your application at the same time. The event loop is called the heart of the node.js. It executes all the callback functions( functions that are called as soon as some work is finished) in a single thread, and it also offloads heavy or expensive tasks like compressing a file to a thread pool. Eventloop makes asynchronous programming possible in node.js. I will talk about asynchronous programming in my next article.

It takes care of all the incoming events and performs the balancing part by offloading heavier tasks into the thread pool and doing the simpler tasks by itself. I will discuss the Event loop again in the following few articles because there's a lot more to it, and it is the essential feature in node.js. You should remember that the Event Loop is the heart of Node js, which makes the node completely different from other backend languages.

Thread Pool

The thread pool gives us four separate threads from the single main thread in the event loop. We can configure it up to 128 lines, but usually, these four are enough. So these four threads together formes a thread pool. The event loop will offload the heavy tasks to the thread pool, which happens automatically behind the scenes. It's not us developers who decide what goes to the thread pool and what doesn't. Some of the expensive tasks that get offloaded are all operations dealing with files, everything related to cryptography, like caching passwords, all compression-related functions, the DNS lookups(matches web domains to their corresponding real IP addresses), and more. This is something that would most easily block the main thread. So, the node takes care of it by automatically offloading them into the thread pool.

For example, when the request hits the node server, it will go to the event loop through the event queue. The event loop will now check whether the task is heavy, like operations dealing with files or network-related operations. If the task is heavy it will offload it to the thread pool, where the thread pool will execute the task separately. it will not block our event loop, and the event loop can perform all the less complex tasks

Other Important Libraries

The most critical libraries for Node.js are LIBUV and V8. However, the node is not only based on V8 and LIBUV but also on a few other libraries like HTTP parser for parsing HTTP, C-ARES for DNS queries, OpenSSL for cryptography, and Zlib for file compression. When all of these components come together ideally, we end up with Node.JS ready to be used on the server-side for all of our applications.

Event-Driven Architecture

Most of the node's core modules, like HTTP File System, are built around an event-driven architecture. The concept is quite simple. In node, there are particular objects called event emitters that emit named events as soon as something important happens in the app, like a request hitting server or a file finishing to read. Event listeners then pick up these events that we developers set up, which will fire off functions(callback functions) attached to each listener.

On the one hand, we have event emitters that will emit named events, and on the other hand, we have event listeners that react to emitted events by calling the callback functions. The event-driven architecture makes it way more straightforward to respond multiple times to the same event. All we have to do is to set up multiple listeners.

Okay, so this is the node's event-driven architecture in a nutshell. Don't worry if this seems a bit too theoretical. I will explain more about the events and callback functions in the following article, and we will also see this logic being used in many situations. So see you in my next article. Have a great day!

Enjoy this post?

Buy Aditya Channe a coffee

More from Aditya Channe