简体   繁体   中英

Live updating Node.js server

I want to design a live updating Node.js Express server, perhaps having a particular route, say /update, which loads a new configuration file. The only concern I have with this right now is that the server could potentially be in any state when the update happens. If I load a new configuration file while there is a JS message being processed for a user request, at the beginning of the user request there could be one configuration and before the request completes there could be a second configuration when the new config file is loaded. The only way I can think of to prevent this is to take down the server for at least one minute (keep the server live, but prevent any incoming requests altogether) and then update the server and put it back online, but then that's not really the best form of hot reloading or live updating is it?

How can I somehow trick the JS event loop so that the config file only gets loaded once all requests have completed and delay any new requests until after the config is loaded?

One algorithm would be:

  1. set a flag "starting re-configuration"
  2. above flag prevents any new requests from being processed (using Express middleware)
  3. check that all current requests are completed (can't think of anything better than a polling loop here)
  4. once above check is done, load new configuration
  5. once configuration is loaded, switch the flag from (1)

Disclaimer: I have not tried this in production. In fact, I have not tried this at all. while I believe the idea is sane, there may be hidden pitfalls along the road which are not currently known to me.

There is one thing that many Node.js developers tend to forget/not fully realise:

There can always be only one JavaScript statement executed at a time.

It does not matter that you can do async I/O or execute a function later in time. No matter how hard you try, all the JS code that you write is executed in a single thread, no parallelism. Only the underlying implementation (which is completely out of our control) can do things in parallel.

This helps us, because as long as our update procedure is synchronous, no other JS code (ie client response) can be executed.

Configuration live-patching

The solution to prevent configuration change mid-request is rather simple:

Each request gets its own copy of the application's configuration.

If your application's configuration lives in a JavaScript object, you simply clone that object for each new request. This means that even if you suddenly change the configuration, it will only be applied to new incoming requests.

There are tons of modules for cloning (even deep cloning) objects, but since I believe mine is best I will use this opportunity for some small self-promotion - semantic-merge .

Code live-patching

This is a bit trickier, but should be generally possible with enough effort.

The trick here is to first remove/unregister current Express routes , clear Node's require cache , require the updated files again and re-register route handlers. Now Express will finish all pending requests using the old code (this is because Node cannot remove these old functions from memory as long as the code contains active object references - which it does - req and res ) and use the newly required modules/routes for new incoming requests. Old code should get released from memory as soon as there are no more requests that started with the old code.

You must not use require anywhere during request processing, otherwise you risk the same problem as with changing configuration mid-request. You can of course use require in a module-level scope because that will be executed when the module itself is required, thus being synchronous.

Example:

// app/routes/users.js (or something)

// This is okay, because it is executed only once - when users.js
// itself is required
var path = require('path')

// This function is something you would put into app.use()
module.exports = function usersRoute (req, res, next) {
  // Do not use require() here! It will be executed per-request!
}

I think that instead of looping a request to the server you can use a Websocket.

That way, when there's a change in the config file that you mentioned, the server can 'emit' a message to the users, so they refresh their data.

If you are using nodeJS and Express, this will help you:

Socket.io with NodeJS

The server will wait for the signal of some user or anybody and emit the signal to all the users, so they get the new data

Node.js:

var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket')(server);
var port = process.env.PORT || 3000;

server.listen(port, function () {
    console.log('Server listening at port %d', port);
});

app.use(express.static("/PATH_TO_PROJECT"));

io.on('connection', function (socket) {
    socket.on('someone update data', function (data) {
        socket.to(socket.room).broadcast.emit('data updated', {params:"x"});
    }
});

Meanwhile, the client will be listening if there's any change:

View.js:

var socket = io();

socket.on('new message', function (data) {
    liveUpdate(data);
});

I hope I understood correctly what you asked

This is a good problem to solve.

A possible solution could be: That you derive the controllers on each path from a parent controller. The parent controller can flag a property ON (a flag / file) when a request arrives and turn it OFF when the response is sent back.

Now subclass this parent controller for each express end-point facing the front end. If you make a request now to '/update', the update controller would know if the server is busy or not through the FLAG and send back a reply if the update was successful or not.

For update failures the front end could possibly post back to the '/update' end point with some back-off scheme.

This scheme might work for you ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM