Graceful closing of node expressjs not working

2019-06-06 08:31发布

问题:

I have a simple problem. I'm trying to shutdown an expressjs nodejs server so that current requests finish their responses and new requests get rejected altogether. I've wittled down my code to just the bare bones so that it'd be easier to understand:

express = require("express")
http = require("http")
app = express()

app.configure ->
    app.set "port", process.env.PORT or 3000
    app.use app.router

ran = false
app.get '/testrun', (req, res)->
    console.log "received request"
    if ran
        res.json
            shouldnt: 'have gotten here'
    else
        ran = true
        setTimeout ->
            res.json
                response: 'fail!'
        , 8000
        console.log 'server closing'
        server.close()
        setTimeout ->
            process.exit()
        , 10000

server = http.createServer(app)
server.listen app.get("port"), ->
    console.log "Express server listening on port " + app.get("port")

So essentially, I make two requests. The first one takes 8 seconds, and the server is closed immediately. So any future requests should be rejected. The second request, however, still gets through. Console output:

$> Express server listening on port 3000
received request
server closing
received request

If you could explain what's going on I would appreciate it.

UPDATE:

Per Benjamin's response, I tried the test code he suggested, and it worked as expected. The problem is in the browser. I edited the original server:

express = require("express")
http = require("http")
app = express()

app.configure ->
    app.set "port", process.env.PORT or 3000
    app.use app.router

ran = false
app.get '/testrun', (req, res)->
    console.log "received request"
    if ran
        res.json
            shouldnt: 'have gotten here'
    else
        ran = true
        setTimeout ->
            res.json
                response: 'fail!'
        , 5000
        console.log 'server closing' # changes here
        server.close ->
            console.log 'closed!'

server = http.createServer(app)
server.listen app.get("port"), ->
    console.log "Express server listening on port " + app.get("port")

Note the changes around server.close. When I run the test code Benjamin suggested, you get the expected output:

$>Express server listening on port 3000
received request
server closing
closed!

But when I open two tabs in chrome, request the first, wait a few seconds, then request the second, I get this output:

$>Express server listening on port 3000
received request
server closing
received request

And without the process.exit, the 'closed' callback never gets called. Kind of weird behavior.

Thanks for your help.

回答1:

This function .close() in express is based on .close in the http module in node which is based on the .close in the net module in nodejs.

Let's see what the API says.

Stops the server from accepting new connections and keeps existing connections. This function is asynchronous, the server is finally closed when all connections are ended and the server emits a 'close' event. Optionally, you can pass a callback to listen for the 'close' event.

This means it's possible you were running the testing code on the same instance of node and close didn't get a chance to run.

Also, setTimeout is not accurate, it's possible that a setTimeout for 8000 miliseconds will fire after 8015 miliseconds of 7980 miliseconds.

When I run the following code to test your code from a different node program:

http = require("http")
http.get "http://localhost:3000/testrun"
setTimeout http.get.bind(http,"http://localhost:3000/testrun"), 8000
setTimeout http.get.bind(http,"http://localhost:3000/testrun"), 9000

I get

$ coffee test.coffee
Express server listening on port 3000
received request
server closing

The other window running the client throws ECONNREFUSED which is what I'd expect