In this article, I will show you how to do a graceful shutdown in a NodeJS application, but first, let's describe what "graceful shutdown" means and why we need to do that in our application and what are the benefits.
Graceful shutdown
Let's imagine you have an HTTP server with NodeJS connected to a database, and every time the server gets called, it sends a request to the database to get/set data which will also be sent to the client by the response.
Imagine you need to shut down the server. The easiest way to kill the server is <Ctrl>+C
. But what if your server didn't finish all the requests? What if some client connections close because we destroyed the server? We will not be able to handle the requests anymore.
-That gives you a point to think, right?
As you might guess, you need to handle all requests and close all resources processing on data(e.e. database connections) and not take any other requests. After that, you can shut down your server with a quiet conscience.
Graceful shutdown - When all of your requests to the server have received a response and there is no remaining data processing work to be done.
Creating a graceful shutdown and shutting down the server in the correct way is essential. You can't know what can happen to the requests made to the server. When you shut down it immediately, you can make a mistake and kill the process, which is doing something important at the moment.
How that works
Here are the four steps to quickly do a graceful shutdown.
- Handle process kill signal
- Stop new requests from client
- Close all data process
- Exit from process
First, we need to handle the server someone wants to shut down. After that, we need to complete all the requests and stop taking new ones from users to the server, so we can be sure that there are no pending requests from users before shutting down the server. After that, we need to close all the data processing (i.e. databases, some file system workers, etc.), it depends on what you are doing on the server, and finally, we can exit from the process.
Let's create a simple NodeJS server and do all the steps that I have mentioned above, which will do a graceful shutdown when we want to close the server and understand how it works.
Here is a simple NodeJS Server example using ExpressJS
Here we have a simple server with a route that creates a user in MongoDB.
We can test the server and create a user in the database using this command
curl -d ‘{ “username”: “Narek” }’ -H “Content-Type: application/json” -X POST http://localhost:3000/user
If you got aSuccess!
message, you could look at the JSON data in MongoDB.
Now let's go through the four steps and write appropriate code.
1. Handle process kill signal
First, let's understand what a process signal is.
A signal is an asynchronous notification sent to a process or to a specific thread to notify an event that occurred.
By emitting the signals NodeJS process will receive events.
Each signal has a name(i.e. 'SIGINT'
, 'SIGTERM'
, etc.), more about that in NodeJS here.
'SIGINT'
Generated by clicking<Ctrl>+C
in the terminal.- The
'SIGTERM'
signal is a generic signal used to cause program termination. Unlike'SIGKILL'
this, signals can be blocked, handled, and ignored. It is the standard way to ask a program to terminate politely. - The shell command
kill
generates'SIGTERM'
by default.
You can read more about Termination Signals here.
As you guess, we need to add a handler to receive a'SIGTERM'
signal.
Here is the following example built on the previous example, which handles the signal.
Now let's try and test it.
Run the server after that. You need to get PID
number, you get that using ps
command, so now you have the number, and you can try to kill the server using this command kill [PID_number]
or just killall node
which will send a signal to all node servers. After running this command, you will get this log from node outputs
SIGTERM signal recived.
If you try again, you will get the same log
SIGTERM signal recived.
-Hmmm, why was the process not killed?
Because you caught the signal but then ignored it.
So, we have done the first thing. Let's move to the next step.
2. Stop new requests from client
Now we need to stop the HTTP server and stop accepting new requests.
We can do that by using theserver.close
function. You can look in the NodeJS doc to get more information about that.
So, the code will look like this.
It will stop accepting new connections to the server, and your request will fail if you try to call the server.
3. Close all data process
In this example, the critical point is to close the MongoDB connection because there aren't left any requests.
So here is how we can close the connection without keeping any active ones.
-Hmmmmmmm, why does the node server exits after the DB connection close.
It's an exciting question, you can try to understand that point yourself, but if you can't or don't want, I will describe it in the next chapter.
4. Close all data process
As you have seen, our application exits a close database connection.
What is the cause? -EventLoop
As we know, NodeJS will exit when the EventLoop queue is empty and there is nothing left to do.
But sometimes, your application can have more functions and will not exit automatically. At this point comes our last work to do.
We need to exit from the process using process.exit
function.
And final graceful shutdown server example will look like this.
Argument 0
means exit with a "success" code.
To exit with a "failure" code use 1
.
To get this exit code after shutdown, run this command in the terminal where you run your node server.
echo $?
By default, NodeJS exits with process code 0
if EventLoop is empty.
That's it!
Summary
It's not the only way to shut down the server correctly. But it's a simple implementation which will work fine on small projects. For some big projects, most likely, you will need server balancing (i.e. Nginx. We can balance the load and not send a request to shut down the server.
Thank you, feel free to ask any questions or tweet me @nairihar
Also follow my “JavaScript Universe” newsletter on Telegram: @javascript