In the traditional approach to creating web servers, for each incoming request or connection the server spawns a new thread of execution or even forks a new process to handle the request and send a response. Conceptually, this makes perfect sense, but in practice it incurs a great deal of overhead.
While spawning threads incurs less memory and CPU overhead than forking processes, it can still be inefficient. The presence of a large number of threads can cause a heavily loaded system to spend precious cycles on thread scheduling and context switching, which adds latency and imposes limits on scalability and throughput.
Much of Node’s utility comes from its large package library, which is accessible from the
npm command. NPM, the Node package manager, is part of the standard Node.js installation, although it has its own website.
Joyent owned, governed, and supported the Node.js development effort for many years. In 2015, the Node.js project was turned over to the Node.js Foundation, and became governed by the foundation’s technical steering committee. Node.js was also embraced as a Linux Foundation Collaborative Project. In 2019, the Node.js Foundation and JS Foundation merged to form the OpenJS Foundation.
Basic Node.js architecture
=>) for the callbacks.
The beginning of the code loads the HTTP module, sets the server
hostname variable to
localhost (127.0.0.1), and sets the
port variable to 3000. Then it creates a server and a callback function, in this case a fat arrow function that always returns the same response to any request:
statusCode 200 (success), content type plain text, and a text response of
"Hello Worldn". Finally, it tells the server to listen on
localhost port 3000 (via a socket) and defines a callback to print a log message on the console when the server has started listening. If you run this code in a terminal or console using the
node command and then browse to localhost:3000 using any Web browser on the same machine, you’ll see “Hello World” in your browser. To stop the server, press Control-C in the terminal window.
Note that every call made in this example is asynchronous and non-blocking. The callback functions are invoked in response to events. The
createServer callback handles a client request event and returns a response. The
listen callback handles the
The Node.js library
As you can see at the left side the figure below, Node.js has a large range of functionality in its library. The HTTP module we used in the sample code earlier contains both client and server classes, as you can see at the right side of the figure. The HTTPS server functionality using TLS or SSL lives in a separate module.
One inherent problem with a single-threaded event loop is a lack of vertical scaling, since the event loop thread will only use a single CPU core. Meanwhile, modern CPU chips often expose eight or more cores, and modern server racks often have multiple CPU chips. A single-threaded application won’t take full advantage of the 24-plus cores in a robust server rack.
You can fix that, although it does take some additional programming. To begin with, Node.js can spawn child processes and maintain pipes between the parent and children, similarly to the way the system
popen(3) call works, using
child_process.spawn() and related methods.
The cluster module is even more interesting than the child process module for creating scalable servers. The
cluster.fork() method spawns worker processes that share the parent’s server ports, using
child_process.spawn() underneath the covers. The cluster master distributes incoming connections among its workers using, by default, a round-robin algorithm that is sensitive to worker process loads.
Note that Node.js does not provide routing logic. If you want to maintain state across connections in a cluster, you’ll need to keep your session and login objects someplace other than worker RAM.
The Node.js package ecosystem
The NPM registry hosts more than 1.3 million packages of free, reusable Node.js code, which makes it the largest software registry in the world. Note that most NPM packages (essentially folders or NPM registry items containing a program described by a package.json file) contain multiple modules (programs that you load with
require statements). It’s easy to confuse the two terms, but in this context they have specific meanings and shouldn’t be interchanged.
You don’t have to use the NPM command line to access the public NPM registry. Other package managers such as Facebook’s Yarn offer alternative client-side experiences. You can also search and browse for packages using the NPM website.
Why would you want to use an NPM package? In many cases, installing a package via the NPM command line is the fastest and most convenient to get the latest stable version of a module running in your environment, and is typically less work than cloning the source repository and building an installation from the repository. If you don’t want the latest version you can specify a version number to NPM, which is especially useful when one package depends on another package and might break with a newer version of the dependency.
For example, the Express framework, a minimal and flexible Node.js web application framework, provides a robust set of features for building single and multi-page, and hybrid web applications. While the easily clone-able Expresscode repository resides at https://github.com/expressjs/express and the Express documentation is at https://expressjs.com/, a quick way to start using Express is to install it into an already initialized local working development directory with the
npm command, for example:
$ npm install express --save
--save option, which is actually on by default in NPM 5.0 and later, tells the package manager to add the Express module to the dependencies list in the package.json file after installation.
Another quick way to start using Express is to install the executable generator
express(1) globally and then use it to create the application locally in a new working folder:
$ npm install -g express-generator@4
$ express /tmp/foo && cd /tmp/foo
With that accomplished, you can use NPM to install all of the necessary dependencies and start the server, based on the contents of the package.json file created by the generator:
$ npm install
$ npm start
CommonJS and ES Modules
require statement. The way to load an ECMA Script Module is to use an
import statement; the module must contain a matching
The latest Node.js has both CommonJS and ES Module loaders. How are they different?
The CommonJS loader is fully synchronous. It’s responsible for handling
require() calls, supports folders as modules, and tries adding extensions (.js, .json, or .node) if one was omitted from the
require() call. The CommonJS loader cannot be used to load ES Modules.
The ECMAScript Module loader is asynchronous. It’s responsible for handling both
import statements and
Copyright © 2022 IDG Communications, Inc.