39

I am little bit confused about the architecture of Node.js

Is this real one ?

enter image description here

First one is correct or second ? Because in second diagram each call passes through V8 first and then Node.js Bindings, But in first one it's vice versa. Can you please help in understanding. Thanks in advance.

Sagar
  • 2,315
  • 7
  • 25
  • 34
  • 2
    Both are correct. Notice the first one doesn't include the JS application, it just shows the layers from which node is built. – Bergi Apr 21 '16 at 11:33
  • @bergi In first one v8 is at the same level where libuv is and in second v8 is processing first and then passing data through nodejs bindings which seems wrong – Sagar Apr 21 '16 at 11:44
  • The node bindings rely both on V8 and libuv, I don't see what's wrong with that. Of course, the dependencies are not that simple in reality, so both diagrams need to do some abstraction. Sure, the second diagram is a bit weird as well as the "application" is executed by V8, not running as a standalone next to it, but how would you draw that? – Bergi Apr 21 '16 at 11:52
  • I didn't draw...I found in one of the post of stack overflow. I just want to confirm that whatever js code we wrote goes from nodejs bindings first and then v8? – Sagar Apr 21 '16 at 12:00
  • No. JS code that calls into the node library will call into the node bindings, which are nothing but hooks installed in V8 to expose them to js. V8 drives this whole process, as it executes the js code. – Bergi Apr 21 '16 at 12:26
  • So we could just say that both diagrams are oversimplified - "wrong", if you want. What do you actually want to know, what do you need this for? – Bergi Apr 21 '16 at 12:27
  • So we can say that whatever piece of code we write directly interpreted by v8 first and then other components comes into picture – Sagar Apr 21 '16 at 12:43
  • Basically I want to know execution flow for any code. How calls passes through architecture – Sagar Apr 21 '16 at 12:44
  • Yes, JS is always interpreted by V8. To see how it got there, or how it talks to the lowerlevel bindings, we need to consider the other components. – Bergi Apr 21 '16 at 12:56

1 Answers1

82

First off, both graphs are correct though the first one is a little outdated. The ascynchronous part of Node.js used to consist of libev, libeio, and libuv. However as libuv advanced over the course of past few years, "[in] the node-v0.9.0 version of libuv libev was removed", leaving libuv to take care of Node.js' entire asynchronous I/O processes (therefore including event loop of course). So the modern version of Node.js architecture would replace the "libeio" and "libev" with "libuv" (as is in the second image).

The reason that the two graphs differ in structure is that they are organized with regards to different perspectives. Graph 1 represents the classification of different pieces of Node.js technology from high-level to low-level (thus it doesn't imply a workflow); whereas graph 2 is the actual workflow of a Node.js operation.

To put this into an analogy: let's say you try to represent different pieces of a car using graphs. You can do this in many ways: you can either organize the different pieces by their classifications/functionalities (Scenario A), thus:

  • power system: engine, oil, cooling, exhaust, etc.
  • transmission system: gear box, shaft, clutch assembly, differential, etc.
  • suspension system: control arm, shock absorber, steering components, etc.
  • ......

or you can also organize the pieces by the workflow (Scenario B):

  • oil --> engine --> transmission --> differential --> suspension --> etc.

(I don't know too much in detail about cars. The name of the pieces and the actual workflow might be wrong. It is only listed to help with understanding.)

Now because the means by which you organize the pieces are different, the order that they appear will differ as well. Scenario A is similar to your graph 1 and Scenario B is similar to graph 2.


I'm not sure about how much you understand the way Node.js works so I'll provide a brief overview of the different pieces that fit into Node.js architecture before moving on to explain the way they interact with each other:

  • V8 - Google's open source JavaScript engine that resides in Chrome/Chromium browsers. Instead of interpreting JavaScript code on the fly like typical web browsers do, V8 translates your JS code into machine code so that it's blazing fast. V8 is written in C++. Read more about how V8 works here.

  • libuv - libuv is originally developed to provide asynchronous I/O that includes asynchronous TCP & UDP sockets, (famous) event loop, asynchronous DNS resolution, file system read/write, and etc. libuv is written in C. Here is a good video to check out to learn more about libuv.

  • Other Low-Level Components - such as c-ares, http parser, OpenSSL, zlib, and etc, mostly written in C/C++.

  • Application - here is your code, modules, and Node.js' built in modules, written in JavaScript (or compiled to JS through TypeScript, CoffeeScript, etc.)

  • Binding - a binding basically is a wrapper around a library written in one language and expose the library to codes written in another language so that codes written in different languages can communicate.

Now the first graph should make sense: on the top is your application (, modules and core Node.js built-in modules) written in JavaScript; on the bottom are Node.js internal components written in C/C++. To bridge them so that they can communicate, you need bindings. So that's where Node.js bindings sit: in between high-level application and low-level Node components. This graphs does not necessarily represent workflow; it's just a classification of different Node.js pieces according to their relations/functionalities to one another.

The second graph represents the actual workflow of a Node.js application. Code written in your application gets compiled by V8. The code communicates with low-level Node.js components via bindings. All the events written in your code are registered with Node.js. Once events are triggered, they are enqueued in the event queue according to the order that they are triggered. As long as there still are remaining events in the event queue, the event loop keeps picking them up, calling their callback functions, and sending them off to worker threads for processing. Once a callback function is executed, its callback is once again send to the event queue, waiting to be picked up by the event loop again.

Part of your confusion might come from the choice of technical terms used in the second graph. If you look closely, below the "NODE.JS BINDINGS" says "(NODE API)", which, unfortunately, are two different things. Node.js API is the interface of its built-in libraries, whereas bindings, from the perspective of software programming, are bridges between codes written in different languages.


A more accurate representation of Node.js' internal structure is this: Node.js Architecture (I downloaded this picture from a source on Internet a while back ago and I forgot where it's from. If the picture belongs to you, please comment and I'll add credit underneath! Thanks!)


Edit: I have recently written a more comprehensive article at explaining Node.js's architecture with an easy-to-understand analogy.

starball
  • 20,030
  • 7
  • 43
  • 238
Aren Li
  • 1,852
  • 1
  • 15
  • 14
  • 1
    Site is offline right now; can't you inline the article in this answer? – Caramiriel Feb 20 '19 at 14:38
  • 5
    The linked Medium article is backed up on the [Web Archive](http://web.archive.org/web/20170208132221/https://arenli.com/architecture-of-node-js-internal-codebase-57cd8376b71f?gi=1984716e61d6) – Alex Aug 26 '19 at 03:49
  • Great answer. But I would like to understand one thing. If NodeJS internally uses worker threads for each event (at C/C++ level), then I believe it's essentially creating a new thread for every new request (event)? If yes, then it will tax the system resources just like other backend frameworks like for eg Java based. So although the Node application code is running on a single thread, on the backend, system will be creating a lot of threads, negating the single thread, "lightweight" advantages of NodeJS. – darKnight Feb 03 '21 at 18:51