Understanding Node.js: An Overview of its Architecture, Features, and Event-Driven Programming.
Today I will try to give a clear idea on Node.Js. Successful use of several web frameworks based on Node.js in back-end/Full-stack development, easy availability of ready-made packages as almost all utility tools, and recent development of many good frameworks in front-end are well suited for back-end, front-end or hybrid app development, such as JavaScript. Explains its effect.
JavaScript is required everywhere to work on this stack, so it is very important to know how JavaScript actually works, or how these tools work. Otherwise, the efficiency of the tools will not be properly utilized or may be misused.
Wikipedia says — “Node.js is an open-source, cross-platform JavaScript run-time environment for executing JavaScript code server-side.”
According to Ryan Dahl, creator of Node.js –
“JavaScript has certain characteristics that make it very different than other dynamic languages, namely that it has no concept of threads. Its model of concurrency is completely based around events.”
So you understand why JavaScript is needed!
Misconceptions About JavaScript and its Relationship with Node.js.
Javascript is integrated with all web browsers. So, we are used to seeing some features that seem to be part of Javascript like — DOM Tree, setTimeout function, AJAX request etc. But they are actually provided by the environment in which JavaScript is running.
In this case, the browser provides these APIs and window, document or such global objects are also initialized by the browser. So if we think of Javascript excluding the features of the browser, then the following features of this language remain -
A. Using functions as first-class citizens. (functions can be passed as arguments, functions can be returned from within one function, etc.).
B. Using the event model (allows for event-driven programming, but also for asynchronous operations).
What is Node.js?
Many of those who are new think that Node JS might be a programming language or framework! But this is completely wrong. Node JS is not a programming language or framework. So what is Node JS?
Node JS is a JavaScript Run-Time Environment. Well, I understand, but what is this Run-Time Environment again?
The function of Run-Time Environment is to run the codes of a specific programming language.
Take a look at the picture below, see if you understand something! If you understand something then you are Genius! Even if you do not understand, there is no problem! Slowly you will understand and I am with you.
Java typically runs through the Java Virtual Machine (JVM). And Java Runtime Environment is required to execute those Java codes.
That is, the Java Runtime Environment knows very well how to correctly run and work with Java code. Don't want to talk much about Java because we are learning Node JS now not Java.I said a little about Java because if you understand this concept of Java, then you can easily understand how Node JS works.
I think before learning something you should understand how it works so that you don't have to go ahead and block it.
How does Node JS work?
As we know J JavaScript was created only for browsers. Previously JavaScript was used only to make websites interactive.
Only browsers can understand JavaScript code. But in 2009, a software engineer named Ryan Dahl brought Node JS to the market and changed everything!
With the advent of Node JS, you can now do a lot with JavaScript!
Every browser has a JavaScript engine that converts JavaScript code into machine code.
Google Chrome has V8 Engine and Mozilla Firefox has SpiderMonkey and each browser has a different JavaScript engine.
The fastest is V8 Engine and it is open source.Ryan Dahl came up with a great idea! He took the V8 engine and created NodeJS, a mix of C and C+.
Just like Java Runtime Environment is needed to run Java code, Node JS is needed to run JavaScript code from outside the browser. Now understand what Runtime Environment is?
Well let me clear it up a bit more. If we write JavaScript code in Node JS, Node JS converts that JavaScript code to C+ code, and from that C+ code to machine code. Finally that machine code understands our machine and gives us output.
So many things happen in the blink of our eyes, we don't understand anything.
And what happens in Java is that the Java Runtime Environment (JRE) runs the Java code and helps the Java Virtual Machine (JVM) understand that code and the Java Virtual Machine converts that code into machine code, and the machine code can be understood by our machine.
In the same way, browsers also convert JavaScript code into C+ machine code through Javascript Engine.
Can you find similarities between Node JS and Java Runtime Environment? Surely this concept is clear to you?
If it's not cleared yet, it will slowly clear up while staying with NodeJS.
Node.js architecture
Simply put, if you have some javascript code with the V8 engine, you can run that code without any problems or dependencies on any other tools.But the sad thing is that from such a setup you could not run any system calls ie file system operations, network connections etc.
The V8 has no power to do these things. Its job is to simply execute the JavaScript code. This is where Node.js comes in handy. With V8, Node.js basically gives us three support:
1) some bindings to work with the system
2) an event loop
3) a thread pool.
(we'll read about the need for event loops and thread pools later)
Just memorize it before Note that, due to the combination of these three things, we get a platform through which non-blocking input/output operations are possible through asynchronous programming. So for a moment, let's think of a simple diagram like the one below.
But the reality is not simple. So please take a look at the figure below, to see the detailed view of Node.js architecture -
At the very top layer is the Node.js API which we can call the core API of Node.js. They are written in Javascript and developers can access these APIs directly from their Node applications.
Below that are the Node.js bindings that actually link the Javascript to the C/C++ library. There are also C/C++ Addons which are basically dynamically linked shared objects and which can be used within Node.js by creating Addons through any C/C++ library.
V8 Engine: An open source JavaScript engine by Google using the C/C++ language originally developed for Google's Chrome web browser. Better to say, it can be used not only as a web browser JavaScript engine but also standalone or embedded in any C/C++ application.
The job of this engine is to convert JavaScript code into machine code. This engine converts JavaScript code directly into machine code through a JIT (Just in Time) compiler implementation. Unlike other engines, it doesn't convert to bytecode or intermediate code, which might need to be interpreted from there.
Other important tasks performed by this engine are - memory management, garbage collection, etc. of the JavaScript program you write.
LibUv: Node JS is really very much focused on the V8 engine and ignores the other parts. But it should be remembered that there are many modules in Node.js one of which is V8 engine and another important part is libuv. In my opinion, the two main components of Node.js are the V8 engine and the libuv library.
Without libuv, many of the key features of the Node.js environment would not exist. However, libuv is a library that supports multi-platform and handles asynchronous input/output operations very nicely on computer systems.
Note that Node.js initially used this library primarily as an abstraction layer on top of the libev and libio libraries. That is, libio and libev used to handle concurrent operations under libuv. But slowly the libuv library itself has been developed to handle concurrency very efficiently.
In this situation, I would like to remind you once again that libuv handles Event Loop and asynchronous I/O or input/output operations where the V8 engine takes responsibility for JavaScript execution.
Some of its important features that you may not know about — Full feature an event loop (depending on the operating system it depends on epoll, kqueue, IOCP, etc.), Asynchronous TCP and UDP sockets, Asynchronous file system operations, thread pools, signal handling and more a lot.
C-ares: A C library for performing multiple DNS queries in parallel avoiding blocking. Since Node.js doesn't depend on Apache or similar web servers and creates its own servers, this tool is needed, right?
HTTP_parser: An HTTP request and response parser, again written in C. It is also necessary because of the previous point.
OpenSSL: An open source implementation for SSL (Secure Sockets Layer) and TLS (Transport Layer Security). It is also known by many as a cryptography library. A networking application will be developed on a platform that does not have it. So the arrival of this module is for good reason.
Zlib: And again a general purpose data compression library written in C.
So these are the necessary modules or parts that Node.js is made of to develop server side or networking applications.
Features of Node.js
We have already got an idea about the architecture of Node.js. In this situation we will discuss its various features and try to understand how they work; I will verify whether it is possible to develop an efficient application according to the definition by using them properly.
Before starting to know what Event Driven Programming actually is -
Event Driven Programming
It is a programming style where the flow of the program depends on certain events. Event means some event, like — mouse click, key press, request for connection to network, response to that request, even sensor output or message received from another program (process/thread) etc.
Node.js has essentially carried this working principal over to the server and has been shown to handle a much larger number of concurrent connections by utilizing non-blocking input/output techniques. So now let's try to understand what non-blocking input/output is and why it is useful.
Blocking and non-blocking I/O
In most software systems, any system call such as — file operation, database query is blocking, i.e. the execution of the main program is stopped while the operation is in progress until the other end of the call is completed and a result is returned.
That is, declare a variable and call a function on its right side, and the function on the other side does various things with the system and returns a value. So until the value of that function is stored in this variable, the program will not have the opportunity to execute the next statements.
You can call it blocking and synchronous programming. One of the goals of Node.js is to utilize as many non-blocking I/O operations as possible. For this it uses event loop and separate thread pool. Although the Node.js runtime itself is single threaded. We will discuss them after reading a little. Before that, let's take a look at threading.
Since server side and networking applications are the main purpose of our discussion, let's discuss a little about Apache web server. Apache is a multi-threaded program i.e. it creates a new thread whenever a request comes to it.
So what's up? If a blocking operation takes place in such a thread, the program running in that thread will stall and unnecessarily occupy valuable system resources. Although apparently it does not seem like much of a problem, but if the number of users increases too much, i.e. if many clients make requests to an Apache server, then Apache will create many threads.
And if every thread has some blocking operation then it will not take time to exhaust all system resources and clients will not get response. Even new clients cannot make requests.
Node.js follows a slightly different style to solve this problem. It serves all requests from a single thread. Your program's code running on this main thread executes synchronously, but whenever a system call occurs, Node.js sends it to an event loop with a callback function.
In this way, the main thread will never freeze and will continue to receive new requests. When the blocking operation in charge of the event loop is finished, the event loop will send the callback function to V8 to execute.
This callback will also have the result of the operation. This way the main thread or main program is not blocked due to blocking operations. So now we have to know this work of event loop in more detail.
Node.js uses a single thread for your code and everything else runs simultaneously concurrently.
This simultaneous behavior is implemented through concurrent operations. It is important to remember that parallel operations and concurrent operations are not the same. Parallel operations are completely separate from each other requiring separate CPU cores.
But concurrent operations can run on single core CPUs by following scheduling or similar techniques to share resources among themselves.Here, I will talk about libuv again because — this complex concurrent operation between worker threads in a thread pool is what libuv does. You don't have to write the algorithm with difficulty.
Event loop
Event Loops in Browsers.
Before understanding the story of Event Loops in Node.js, let's take a look at the event loop in web browsers that we are always using without knowing it. By doing this, it will be easier to understand the event loop of Node.js.
If you have ever written code like “OnClick(alert(‘Hello’))” then you have already used event loop. In this case you have registered a callback function called alert('Hello') with the browser's 'click' event.
Then this callback function is executed when clicked. Node.js takes this principal to the server side with libuv. Remember, we learned that the event loop occurs within libuv?
The browser's JavaScript runtime contains a message queue or callback queue. Its queue contains some messages and associated callback functions.
Whenever an event occurs such as a mouse click, a response to a request or even the expiration of a settimeout timer; A message or callback is then deposited into that queue. The event loop then picks up the first callback from that queue and pops it onto V8's call stack.
And if that function is currently on the top of the call stack, V8 executes it normally. Before proceeding further, since the call stack term has come up, it is better to say two lines about it. Will work later.
Here the Call Stack is a data structure within the V8 engine that essentially keeps track of which functions in your program are currently executing. It should be noted that the call stack is one of the two main components of the V8 engine and the heap is for memory management.
Example:
function init() {
var link = document.getElementById("foo");
link.addEventListener("click", function changeColor() {
this.style.color = "burlywood";
});
}
init();
According to this program, when a user clicks on the foo element i.e. fires the click event, a message is stored in the message queue along with a callback (changeColor).
Then the event loop (which is a semi-infinite loop) dequeues or pops the messages in this queue and leaves the call stack to the main JavaScript engine. And if the call stack is free, it executes it as soon as it is found.
Let's look at another example to understand the responsibilities of the event loop again:
console.log(“Hello”);
setTimeout(function(){
alert("JS");
}, 3000);
console.log(“World”);
What happens if this program goes to the V8 engine? Since the first console logging function is not a blocking operation, the call stack will execute it very quickly and output Hello will be logged.
Next, the setTimeou() function will accumulate on the call stack. When entering it, there will be a special function or API call provided by the browser. It contains a callback function. But it is not associated with any event.
In this case, a timer of 3 seconds can be considered as an event. So, the browser will wait for that function with respect to a timer.(This happens in the red part shown on the right in the figure above.
You can think of that area as the Web API area, which is a convenient feature for the JavaScript runtime on the part of the browser.) By now the setTimeout function will be removed from the call stack (since it no longer contains Nothing or no return) and log function will stack to print World to the console and print it when finished.
On the other hand, when the 3 second timer expires, the callback function will arrive at the message queue and be deposited.Then the endlessly looping event loop will push it onto the call stack whenever it sees it, and the call stack will now get the alert() function and execute it.
And so while running the whole program, Hello will be printed first, then World will be printed without stopping the execution despite a blocking or slow operation. Then JS will print. Thus we have seen how non-blocking execution can be achieved by asynchronous programming through event loops.
Event Loops in Node.js.
Since we have a little idea of how event loops work in the browser, let's move on to its implications and style of work in Node.js. Basically, this event loop in libuv and the accompanying thread pool help Node.js get the ability to handle many concurrent requests even if Node.js runs on a single thread.
Node.js' event loop basically involves an event queue and a thread pool. In this case, Node.js' core APIs maintain communication with this libuv through Node.js bindings (when non-blocking I/O operations are needed later). libuv basically registers functions with various events and dispatches the corresponding callback function to a non-blocking worker pool or thread pool,When an event occurs.
See a NodeJS program:
var http = require('http');
var server = http.createServer().listen(3000);
server.on('request', function (request, response) {
response.end('Hello World!');
});
console.log("Listening on port 3000...")
(Note: Although this code is not written like this, it is written like this to indicate the existence of the request event.)
First, running this code will only show “Listening on port 3000…” in the console because the previous statements are event driven and no such event has occurred so far.
The first time this code is executed, Node.js registers a request event (more than one event may be registered depending on the type of program). When this script finishes executing, Node.js enters the event loop.
Meanwhile, an event is submitted to its event loop that is “on request” and has a callback attached to it.That is, this event loop will use V8's call stack to execute the callback when the "request" event occurs. And we can hit the URL http://localhost:3000 in the browser or terminal (with curl) to trigger the request event.
Then a request event will occur on the server and its callback will be deposited in the event queue.Then the event loop will pick up that callback from there and send it to V8 for execution. In this case the callback function will write a response message and stop.
No matter how many requests come to this server now, they will trigger the 'request' event and be deposited into the event queue with its callback function. Again the event loop will take this callback and send it for execution.This will continue.
Now let's see the following program -
var http = require('http'),
fs = require('fs');
var server = http.createServer().listen(3000);
server.on('request', function (request, response) {
fs.readFile('file.txt', 'utf-8', function (err, data) {
response.end(data);
});
});
console.log("Listening on port 3000...");
In this case the same thing will happen as before but here there is a blocking operation (file read) inside the request event callback function. And so when the request event callback executes, the event loop will delegate the file operation within it to the thread pool.
A thread in the thread pool then appropriately completes the file operation and pushes another callback associated with that file operation to the event queue (along with the file data). Then, as before, this inner callback will complete its execution when it exits the event-queue.
A question can come here that if Node.js is using multiple threads at the end then how is it different? In fact, Node.js handles all requests from a single thread, and splits blocking operations into separate threads. Here no thread is sitting idle and the main thread is not frozen for its idle sitting.
One thing to keep in mind is that Node.js single and main threads are blocked at the start if they are intentionally frozen using blocking code. This thread cannot accept new requests.
See the diagram below-
Whice shows the communication of the V8 engine from the user application. Then the relationship with libuv and the event loop through the Node API. Again communication between libuv's event queue and thread pool / worker threads. Then all return path to the user application. No major module of entire Node.js has been skipped here.
Pros and cons of using Node.js.
Convenience
A. By using asynchronous I/O, application development based on user requests is possible.
B. Since Node.js is built on top of JavaScript, development is very fast using one language for both backend and frontend. No need for a technology switch.
C. Unlike other stacks, there is no need to know about deployment-based configuration with separate server tools because Node.js has built-in server technology and deployment is easy.
D. Due to active community contributions, many ready packages are being added to repositories like npm which can be used to develop almost all kinds of tools and software.
Difficulty
A. Difficulty with CPU intensive tasks such as report generation or analytics-based software development (although this problem can be solved by distributing threads across multiple cores)
B. Lack of clear understanding of event driven methodology and poor quality coding for it can lead to poor quality software. For example — not being able to properly manage callback hell.
0 Comments