Here are some examples of non-blocking i/o operations:
- file reading
- database operations
- http requests
- bash execution
On the other hand, there are time consuming tasks which blocks execution (heavy computation), e.g.:
- factorial computation
- calculating fibonacci series
- processing large data
It’s worth to say that almost all operations which are regular web servers expected to do are i/o.
Back to nodejs performance, let’s say I have 3 routes in my rest API:
- ‘/index‘ – prints “Hello world!” (takes 3ms)!
- ‘/factorial‘ – computes factorial of some big number – blocking (takes 3000ms)
- ‘/fractal‘ – request db to compute bit fractal – non blocking (takes 3000ms)
If I make requests in this order:
I will get response:
// waits 3 seconds
I had to wait 3 seconds to get “Hello World” responses. What happened is that factorial operation blocked all execution and other requests had to wait until it finished. It is too slow and if I had many clients it would be practically not usable.
If I make similar requests but for fractal:
and I will get response:
// waits 3 seconds
***** pretty fractal from db *****
Fractal operation did not block other requests. It created query to the database, waited (but not blocked) until it’s finished and then sent response back to the client. If your server was under heavy load, other clients would not be affected and the responsivity would be great. And what is the best you don’t need any threads to serve tousands of clients concurrently.
If you despite all above need to run time consuming tasks on your server, there are several options.
When new worker is created library creates completely new process and runs it with given source code. It is possible to send and receive data between server and worker. It is also possible to create pools of workers, which is a cool feature when you don’t want to break down your server.
Main drawback is that workers runs in completely different context as server. For more info look into documentation.
New versions of nodejs provides Cluster API. It is possible to fork current process and run multiple instances of the server. The main process is called ‘Master’ and the forked processes ‘Workers’ (similiarly to worker threads).
Basic use case for use of node cluster is, let’s say, request load balancing between workers to utilize multiple CPUs. It is possible to use this API and implement workers which runs time consuming tasks.
Currently node cluster does not support thread pooling, so you have to implement it yourself.
Nodejs is one of the most performant webservers in these days. It’s single threaded nature does not stop it to serve tousands of clients at time with a great responsivity. However it’s important to understand which type of tasks is nodejs dedicated for – non-blocking i/o operations. If you need to run time consuming tasks despite all of this, you can use webworker-threads library or built-in node cluster API.