I am trying to make gracefully shutdown in node.js using express 4.x http server.
Closing express server is easy but what worries me is that we have a lot of async jobs. Example of the flow :
Receive request
Do some stuff
Send response back to the client
In background continue to do some async stuff related to that request like making another request to some third part service
Receive response from third part service and save response to database etc.
Finish
So if I make my gracefully shutdown code like this :
process.on('SIGTERM', function () {
serverInstance.close(function(){
closeConnectionToDatabases(function(){
process.exit(0);
})
});
// shutdown anyway after some time
setTimeout(function(){
process.exit(0);
}, 8000);
});
How can I be sure that everything goes ok if SIGTERM has happened between first and second step in flow explained above? What about fourth, fifth and sixth step? Is there any nice way to handle this or it is just about manually watch to all requests going from your service in async way and wait for them?
Thanks,
Ivan
process.exit() will terminate node.js process immediately, without waiting for all aync tasks to finish.
If you want a truly graceful shutdown, you should close all resources (all open connections and file descriptors) manually, instead of calling process.exit(). In this case node.js will terminate immediately after finishing all pending async tasks.
So, just close all database connections and other i/o resources:
process.on('SIGTERM', function () {
serverInstance.close(function(){
closeConnectionToDatabases(function(){
// now node.js should close automatically
})
});
// shutdown anyway after some time
setTimeout(function(){
process.exit(0);
}, 8000);
});
Here's a better option:
Instead of using setTimeout to wait for all the async jobs to finish, we can create a new Promise, and resolve it when the async jobs are completed. Then after the promise is resolved, we can exit the process.
process.on('SIGTERM', () => {
new Promise((resolve) => {
serverInstance.close(async () => {
await closeConnectionToDatabases();
resolve();
});
}).then(() => process.exit(0))
});
Hope it helps you :)
Related
I'm using pm2 to reload my app but in my test the active requests are cancelled by the reload.
I tested by making an endpoint with an await of 10 seconds, requesting a response to this endpoint with Insomnia and then, before the 10 seconds await completes, reloading the app with pm2 reload . But what happens is that the pm2 reload stops the Insomnia request, which finishes as "Error: Server returned nothing (no headers, no data)".
Am I doing something wrong? Was not pm2 supposed to identify current running requests on await state? It could be a real production request where the app was waiting for a database response.
You can catch the SIGTERM signal from within your node.js/express application:
process.on('SIGTERM', () => {
console.info('SIGTERM signal received.');
console.log('Close http server.');
server.close(() => {
console.log('Http server closed.');
// You can close here other things that needs to, for example a mongodb/mongoose connection
mongoose.connection.close(false, () => {
console.log('MongoDb connection closed.');
process.exit(0);
});
});
});
I need to send an email when server is shutting down, I'm using nodemailer but email is not sending when I write it before process.exit().
sendMail('Server is shutting down')
process.exit();
I was trying to use "beforeExit" event but it's not working either.
The 'beforeExit' event is not emitted for conditions causing explicit termination, such as calling process.exit() or uncaught exceptions.
As I understand as per the Doc.
Listener functions must only perform synchronous operations. The Node.js process will exit immediately after calling the 'exit' event listeners causing any additional work still queued in the event loop to be abandoned. In the following example, for instance, the timeout will never occur:
process.on('exit', (code) => {
setTimeout(() => {
console.log('This will not run');
}, 0);
});
On exit event it requires a sync call but nodemailer is async.
Node mail has a callback mechanism transporter.sendMail(data[, callback]), you can transfome that to a Promise
then you you will be able to do this in your code:
sendMail('Server is shutting down')
.then(()=>{
process.exit();
})
or you can add and await by transforming you function to an async function
as a results for one of the two solution process.exit(); will be called only after the sendMail function callback has excuted (=mail added to the queue of Postfix for example)
Consider a Node.js application with few processes:
single main process sitting in the memory and working like a web server;
system user's commands that can be run through CLI and exit when they are done.
I want to implement something like IPC between main and CLI processes, and it seems that ZeroMQ bindings for Node.js is a quite good candidate for doing that. I've chosen 6.0.0-beta.4 version:
Version 6.0.0 (in beta) features a brand new API that solves many fundamental issues and is recommended for new projects.
Using Request/Reply I was able to achieve what I wanted: CLI process notifies the main process about some occurred event (and optionally receives some data as a response) and continues its execution. A problem I have right now is that my CLI process hangs if the main process is off (is not available). The command still has to be executed and exit without notifying the main process if it's unable to establish a connection to a socket.
Here is a simplified code snippet of my CLI running in asynchronous method:
const { Request } = require('zeromq');
async function notify() {
let parsedResponse;
try {
const message = { event: 'hello world' };
const socket = new Request({ connectTimeout: 500 });
socket.connect('tcp://127.0.0.1:33332');
await socket.send(JSON.stringify(message));
const response = await socket.receive();
parsedResponse = JSON.parse(response.toString());
}
catch (e) {
console.error(e);
}
return parsedResponse;
}
(async() => {
const response = await notify();
if (response) {
console.log(response);
}
else {
console.log('Nothing is received.');
}
})();
I set connectTimeout option but wonder how to use it. The docs state:
Sets how long to wait before timing-out a connect() system call. The connect() system call normally takes a long time before it returns a time out error. Setting this option allows the library to time out the call at an earlier interval.
Looking at connect one see that it's not asynchronous:
Connects to the socket at the given remote address and returns immediately. The connection will be made asynchronously in the background.
Ok, probably send method of the socket will wait for connection establishment and reject a promise on connection timeout...but nothing happens there. send method is executed and the code is stuck at resolving receive. It's waiting for reply from the main process that will never come. So the main question is: "How to use connectTimeout option to handle socket's connection timeout?" I found an answer to similar question related to C++ but it actually doesn't answer the question (or I can't understand it). Can't believe that this option is useless and that it was added to the API in order to nobody can't use it.
I also would be happy with some kind of a workaround, and found receiveTimeout option. Changing socket creation to
const socket = new Request({ receiveTimeout: 500 });
leads to the the rejection in receive method and the following output:
{ [Error: Socket temporarily unavailable] errno: 11, code: 'EAGAIN' }
Nothing is received.
Source code executed but the process doesn't exit in this case. Seems that some resources are busy and are not freed. When main process is on the line everything works fine, process exits and I have the following reply in output:
{ status: 'success' }
So another question is: "How to exit the process gracefully on rejecting receive method with receiveTimeout?". Calling process.exit() is not an option here!
P.S. My environment is:
Kubuntu 18.04.1;
Node 10.15.0;
ZeroMQ bindings are installed this way:
$ yarn add zeromq#6.0.0-beta.4 --zmq-shared
ZeroMQ decouples the socket connection mechanics from message delivery. As the documentation states connectTimeout only influences the timeout of the connect() system call and does not affect the timeouts of sending/receiving messages.
For example:
const zmq = require("zeromq")
async function run() {
const socket = new zmq.Dealer({connectTimeout: 2000})
socket.events.on("connect:retry", event => {
console.log(new Date(), event.type)
})
socket.connect("tcp://example.com:12345")
}
run()
The connect:retry event occurs every ~2 seconds:
> node test.js
2019-11-25T13:35:53.375Z connect:retry
2019-11-25T13:35:55.536Z connect:retry
2019-11-25T13:35:57.719Z connect:retry
If we change connectTimeout to 200 then you can see the event will occur much more frequently. The timeout is not the only thing influencing the delay between the events, but it should be clear that it happens much quicker.
> node test.js
2019-11-25T13:36:05.271Z connect:retry
2019-11-25T13:36:05.531Z connect:retry
2019-11-25T13:36:05.810Z connect:retry
Hope this clarifies the effect of connectTimeout.
I have seen some questions about sending response immediately and run CPU intensive tasks.
My case is my node application depends on third party service responses so the process flow is
Node receives request and authenticates with third-party service
Send response to user after authentication
Do some tasks that needs responses from third party service
Save the results to database
In my case there is no CPU intensive tasks and no need to give results of additional tasks to the user but node needs to wait for responses from third-party service. I have to do multiple req/res to/from the third-party service after the authentication to complete the task.
How can I achieve this situation?
I have seen some workarounds with child_process, nextTick and setTimeOut.
Ultimately I want to send response immediately to user and do tasks related to that user.
Thanks in advance.
elsewhere in your code
function do_some_tasks() { //... }
// route function
(req, res) => {
// call some async task
do_some_tasks()
// if the above is doing some asynchronous task, next function should be called immediately without waiting, question is is it so?
res.send()
}
// if your do_some_tasks() is synchronous func, the you can do
// this function call will be put to queue and executed asynchronously
setImmediate(() => {
do_some_tasks()
})
// this will be called in the current iteration
res.send(something)
Just writing a very general code block here:
var do_some_tasks = (req, tp_response) => {
third_party_tasks(args, (err, result)=<{
//save to DB
});
}
var your_request_handler = (req,res) => {
third_party_auth(args, (tp_response)=>{
res.send();
//just do your tasks here
do_some_tasks(req, tp_response);
});
}
This Node.js server will shutdown cleanly on a Ctrl+C when all connections are closed.
var http = require('http');
var app = http.createServer(function (req, res) {
res.end('Hello');
});
process.on('SIGINT', function() {
console.log('Closing...');
app.close(function () {
console.log('Closed.');
process.exit();
});
});
app.listen(3000);
The problem with this is that it includes keepalive connections. If you open a tab to this app in Chrome and then try to Ctrl+C it, it won't shutdown for about 2 minutes when Chrome finally releases the connection.
Is there a clean way of detecting when there are no more HTTP requests, even if some connections are still open?
By default there's no socket timeout, that means that connections will be open forever until the client closes them. If you want to set a timeout use this function: socket.setTimeout.
If you try to close the server you simply can't because there are active connections, so if you try to gracefully shutdown the shutdown function will hang up. The only way is to set a timeout and when it expires kill the app.
If you have workers it's not as simple as killing the app with process.exit(), so I made a module that does extacly what you're asking: grace.
You can hack some request tracking with the finish event on response:
var reqCount = 0;
var app = http.createServer(function (req, res) {
reqCount++;
res.on('finish', function() { reqCount--; });
res.end('Hello');
});
Allowing you to check whether reqCount is zero when you come to close the server.
The correct thing to do, though, is probably to not care about the old server and just start a new one. Usually the restart is to get new code, so you can start a fresh process without waiting for the old one to end, optionally using the child_process module to have a toplevel script managing the whole thing. Or even use the cluster module, allowing you to start the new process before you've even shut down the old one (since cluster manages balancing traffic between its child instances).
One thing I haven't actually tested very far, is whether it's guaranteed safe to start a new server as soon as server.close() returns. If not, then the new server could potentially fail to bind. There's an example in the server.listen() docs about how to handle such an EADDRINUSE error.