By: +David Herron; Date: 2016-05-01 09:29
We have to first go over some basic principles about Node.js, and then look at a method ...
Node.js is a single-threaded system that does not allow the programmer to create more threads. Other platforms support threads so that the application can fill more CPU cores but Node.js was purposely designed around a different paradigm. Thread-based systems are notoriously difficult to work with ...
Using threads to implement concurrency often comes with admonitions like these: "expensive and error-prone", "the error-prone synchronization primitives of Java", or "designing concurrent software can be complex and error prone". The complexity comes from the access to shared variables and various strategies to avoid deadlock and competition between threads. The "synchronization primitives of Java" are an example of such a strategy, and obviously many programmers find them hard to use. There's the tendency to create frameworks such as java.util.concurrent to tame the complexity of threaded concurrency, but some might argue that papering over complexity does not make things simpler.
Node.js asks us to think differently about concurrency. Callbacks fired asynchronously from an event loop are a much simpler concurrency model; simpler to understand, and simpler to implement. That's the Node.js design paradigm. To have a light-weight event loop sending and receiving events to/from the file system, the network, or anywhere else. That any long-running operation should defer to the event loop, so that other events can be handled while waiting for the response from a database, the file system, or a REST service.
The high performance Node.js enjoys is due to that design model.
The end result is the Node.js programmer has in front of them a single process, running a single thread, containing an efficient event loop.
The question then is how to have this scale to fill out the cores on a single server?
Built into Node.js is the Cluster module: https://nodejs.org/api/cluster.html
It's billed as: "A single instance of Node.js runs in a single thread. To take advantage of multi-core systems the user will sometimes want to launch a cluster of Node.js processes to handle the load. The cluster module allows you to easily create child processes that all share server ports."
Some libraries to make the task simpler are:
- Recluster: https://www.npmjs.com/package/recluster
- Cluster Fork: https://www.npmjs.com/package/clusterfork
- yacm: https://www.npmjs.com/package/yacm
- Easy Cluster: https://www.npmjs.com/package/easy-cluster
- node-daemon: https://www.npmjs.com/package/node-daemon
- Worker Monitor: https://www.npmjs.com/package/worker-monitor
- herd: https://www.npmjs.com/package/herd
- flora-cluster: https://www.npmjs.com/package/flora-cluster
- Teamster: https://www.npmjs.com/package/teamster
- cfork: https://www.npmjs.com/package/cfork
- Fleet: https://www.npmjs.com/package/fleet
There are no doubt other packages in this area: https://www.npmjs.com/browse/keyword/cluster
The idea is to create several processes, and to pass incoming socket connections to those processes, distributing them so the workload on each CPU core is fair. The built-in Cluster module can do a lot of this, and these 3rd party solutions build on that to make it easier or more reliable.
While that can fill the cores on a given server, what if your workload requires multiple servers?
This is where you need to learn about the systems to automate server deployment.
In the 3rd edition of my book - which I'm just finishing - Node.js Web Development - I spend two chapters going over Docker. It's a system for describing a virtualized server application. It creates a "container image" which allows you to easily deploy one or more service containers on one or more Docker hosts. To scale a Dockerized Node.js app across many servers requires tools, like Docker Swarm, that build on Docker.