tylerstillwater / graceful Goto Github PK
View Code? Open in Web Editor NEWGraceful is a Go package enabling graceful shutdown of an http.Handler server.
License: MIT License
Graceful is a Go package enabling graceful shutdown of an http.Handler server.
License: MIT License
Hi,
this is really a minor issue but the two examples in the README file cannot be copy/pasted and compiled out of the box.
An updated README can be found here: sharkone@264135b and I can create a pull request if needed.
every time a http connection opens or closes a new count is sent inside a go routine to the active channel. that means for every connection in the lifetime of the server these go routine stacks (n_2_8 kb) are soon enough to kill the server doing exactly the opposite of what is advertised:
to better understand
you can test this with a simple server as from the readme
$ go run ./server.go
$ for ((x=0; x<=500; x++)) do curl -s --no-keepalive http://localhost:3001 > /dev/null ; done
then send a sigquit signal (Ctrl-) to the terminal running the server and see about 2000 go routines using up 8kb (4kb soon again) each
I am using the following code:
package main
import (
"crypto/tls"
"fmt"
"gopkg.in/tylerb/graceful.v1"
"net/http"
"time"
)
func main() {
cert := []byte("-----BEGIN CERTIFICATE-----...")
key := []byte("-----BEGIN RSA PRIVATE KEY-----...")
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "Welcome to the home page!")
})
xcert, err := tls.X509KeyPair(cert, key)
if err != nil {
panic(err)
}
tlsConf := &tls.Config{
Certificates: []tls.Certificate{xcert},
}
tlsConf.BuildNameToCertificate()
srv := &graceful.Server{
Timeout: 0,
Logger: graceful.DefaultLogger(),
Server: &http.Server{
Addr: ":https",
Handler: mux,
TLSConfig: tlsConf,
},
}
srv.ListenAndServeTLSConfig(tlsConf)
}
I am using go 1.6 and Chrome 50 (latest) on Windows 10 64-bit.
Here are the steps to reproduce:
https://localhost
.Non-http servers seem to correctly exit after sometime if timeout is set to 0.
I start the server with ListenAndServe
as follows:
server := &graceful.Server{
Timeout: 10 * time.Second,
Server: &http.Server{
Addr: fmt.Sprintf("%s:%d", s.ListenIp, s.ListenPort),
ReadTimeout: time.Duration(5) * time.Second,
Handler: s,
},
}
err := server.listenAndServe()
When I terminate the process with SIGTERM
sent via ^C
I get the following race:
==================
WARNING: DATA RACE
Read by main goroutine:
github.com/stretchr/graceful.(*Server).Serve()
/home/dcelasun/go/src/github.com/stretchr/graceful/graceful.go:249 +0xa6c
github.com/stretchr/graceful.(*Server).ListenAndServe()
/home/dcelasun/go/src/github.com/stretchr/graceful/graceful.go:110 +0x2ad
github.com/redacted/server.(*Server).Start()
/home/dcelasun/go/src/github.com/redacted/server/server.go:88 +0x7c8
main.main()
/home/dcelasun/go/src/github.com/redacted/main.go:72 +0xd92
Previous write by goroutine 13:
github.com/stretchr/graceful.func·004()
/home/dcelasun/go/src/github.com/stretchr/graceful/graceful.go:273 +0x9d
sync.(*Once).Do()
/build/go/src/go-1.4.2/src/sync/once.go:44 +0xe6
github.com/stretchr/graceful.(*Server).StopChan()
/home/dcelasun/go/src/github.com/stretchr/graceful/graceful.go:275 +0x84
github.com/redacted/server.(*Server).Shutdown()
/home/dcelasun/go/src/github.com/redacted/server/server.go:192 +0x35
Goroutine 13 (running) created at:
github.com/redacted/server.(*Server).Start()
/home/dcelasun/go/src/github.com/redacted/server/server.go:85 +0x777
main.main()
/home/dcelasun/go/src/github.com/redacted/main.go:72 +0xd92
==================
I'm blocking on theStopChan
channel in a separate goroutine:
go s.Shutdown(server)
func (s *Server) Shutdown(server *graceful.Server) {
<-server.StopChan()
s.Close() // Closes other stuff, not relevant to this bug
}
Sometimes I need to have access to underlying listener (say listening at ":0". In this case I want to print listener.Addr()).
Since this commit 6dd9b0d http2 is enabled by default with Go 1.7.1.
How can I disabled it, cause MS Edge have SCRIPT7002: XMLHttpRequest: Network Error 0x2eff.
Instead of taking just a *negroni.Negroni
you should support generic http.Handler
servers. :)
https://github.com/stretchr/graceful/blob/master/graceful.go#L35
The os.Signal channel is global. This causes a race condition when multiple servers are run at the same time.
A Stop()
function should be provided to stop the server without needing an os.Signal.
I'd like to discuss solidifying v1 API and releasing it using gopkg.in. This would require us to be backward compatible through major releases. I want to get much better about proper releases and compatibility through versions. gopkg.in facilitates that, as well as making consistent, reproducible builds possible.
@mb0 any thoughts on this?
Looks like graceful doesn't track connections going to StateIdle and waits for them to completely timeout. This means servers with very long idle timeouts always have to wait for their connections to timeout or set a more aggressive timeout for the graceful shutdown. The more aggressive timeout is not quite what we want since that could shut down the server while there are requests still in flight.
It would be nice for graceful to track idle connection states, close them when shutdown is initiated, and not count them against the server being able to close.
Hi,
Having trouble finding a bug that's causing a File Descriptor leak with a bittorrent tracker based on graceful:
https://github.com/chihaya/chihaya/blob/develop/http/http.go#L113
(I'm working in the develop branch)
I can't pin-point whats causing it but something is causing the daemon to at random intervals keep eating FD's until it hits a system limit, then fails health checks, thus getting restarted then working again until the bug is hit again. We tried setting a ReadTimeout and WriteTimeout (http://pastebin.com/zZLmqBYN) but this did not help.
Please let me know what other information that could help. Would love any pointer of things to try. Thankyou in advance.
there is concurrent write and read access possible on timeout condition.
please run go test -race to reproduce
This issue on etcd explains the issue.
Hi, finding odd behavior here and think it's a bug. On Centos6, go version go1.6.2 linux/amd64. Sample program at bottom. Basically all this does is create the server with a handler that artificially takes 2s to process, with a graceful timeout of 5s.
Then it sends a synchronous request, reads the response (so no shutdown involved yet), then another in a goroutine and calls Stop()
.
Expected behavior: with a 5s Timeout on graceful.Server, it should let the running request (which should take 2s) finish successfully.
Expected output:
Error (after 2.005738855s): <nil>
Actual behavior: though the program takes 2s to actually quit, the request terminated immediately.
Actual output:
Error (after 102.433484ms): Get http://localhost:3001/: dial tcp [::1]:3001: getsockopt: connection refused
Note that the first synchronous request makes a difference. If you comment that out, then it behavior as expected. I suspect this has something to do with srv.Server.ConnState
not handling StateActive.
package main
import (
"fmt"
"io/ioutil"
"net"
"net/http"
"sync"
"time"
"github.com/tylerb/graceful"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
time.Sleep(2 * time.Second)
fmt.Fprintf(w, "done")
})
s := &graceful.Server{
Server: &http.Server{
Handler: mux,
},
Timeout: 5 * time.Second,
}
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
lp, err := net.Listen("tcp", ":3001")
if err != nil {
panic(fmt.Sprintf("Failed to listen: %v", err))
}
s.Serve(lp)
}()
// Ensure we are listening on the port
time.Sleep(100 * time.Millisecond)
// Make a sample first request. The connection will be left idle.
resp, err := http.Get("http://localhost:3001/")
if err != nil {
panic(fmt.Sprintf("first request failed: %v", err))
}
ioutil.ReadAll(resp.Body)
resp.Body.Close()
start := time.Now()
wg.Add(1)
go func() {
defer wg.Done()
_, err := http.Get("http://localhost:3001/")
fmt.Printf("Error (after %v): %v\n", time.Now().Sub(start), err)
}()
// Ensure the request goes out
time.Sleep(100 * time.Millisecond)
s.Stop(5 * time.Second)
wg.Wait()
}
The codebase is so small that it should be possible to ensure 100% test coverage. Currently it is at 93.5%, but that is prior to the incoming PR #14.
I have an application in which an HTTP server binds to privileged ports, then drops privileges and starts serving. In order to do this, I have to create my own net.Listener. This is rather cumbersome and doesn't take advantage of connection limiting.
Calling .ListenAndServe() before privilege dropping creates a race condition, a brief window of time where HTTP requests could be served by a non-privilege-dropped connection.
What would be nice is some sort of .PreListen() method which opens the listener but does not serve. If .ListenAndServe() is then called, it uses the existing listener if .PreListen() was called.
If I wrote this code, would you be interested in merging it?
Would you be open to a BeforeShutdown callback? ie:
type Server struct {
...
// BeforeShutdown is called before the listener is closed.
BeforeShutdown func()
...
}
...
func (srv *Server) handleInterrupt(interrupt chan os.Signal, listener net.Listener) {
<-interrupt
if srv.BeforeShutdown != nil {
srv.BeforeShutdown()
}
srv.SetKeepAlivesEnabled(false)
_ = listener.Close() // we are shutting down anyway. ignore error.
if srv.ShutdownInitiated != nil {
srv.ShutdownInitiated()
}
srv.stopLock.Lock()
signal.Stop(interrupt)
close(interrupt)
srv.interrupt = nil
srv.stopLock.Unlock()
}
I can write a pull request in a few minutes, alternatively if you just added it too :)
This would allow some round-trip communication for servers that have a two-way connection.
It looks like in https://github.com/tylerb/graceful/blob/master/graceful.go#L175-L177, the beginning of the connection is timed, but that timing is later removed here https://github.com/tylerb/graceful/blob/master/graceful.go#L181-L183 and never used. Any reason why that code is there? It would be awesome to expose the connection timing to the ConnState
handler here https://github.com/tylerb/graceful/blob/master/graceful.go#L186
Let's say we start three servers on different ports. Will they all get the SIGINT? Or will only the most recently started one get it?
I just discovered that unfortunately graceful does not work for my use case.
I want it to wait for long lived websocket connections. But those connections seem to be discounted as soon as they are hijacked
. So they don't really count as active connections at all.
I'm not really familiar to the internal workings of the http servers connection handling but looking at the code and specifically at 33d2033 it seems to me there is no easy way of making graceful work for highjacked connections. Is this correct or is there a way to make graceful wait for all connections the http server?
Hey @tylerb I wrote https://github.com/pressly/valve/blob/master/_example/main.go recently to work with tylerb/graceful to signal the shutdown of the server to other goroutines and manage a waitgroup to wait for things to finish off. If you have a sec to check it out I'd appreciate it and if you have any feedback too. Cheers
I have a simple https webserver like so:
package main
import (
//"gopkg.in/tylerb/graceful.v1"
"net/http"
)
func main() {
srv := &http.Server{Addr: ":https"}
srv.ListenAndServeTLS("cert.crt", "key.pem")
}
If I build it and run in and then access it via Chrome, I can see that it uses http/2 (h2) from the dev tools.
If I add graceful into the mix, it only uses http/1.1:
package main
import (
"gopkg.in/tylerb/graceful.v1"
"net/http"
"time"
)
func main() {
srv := &http.Server{Addr: ":https"}
graceful.ListenAndServeTLS(srv, "cert.crt", "key.pem", 10*time.Second)
}
The current implementation hard codes the signal handling that the graceful shutdown reacts to. In my use case, I want to be able to stop the server with and without signal handling. This is especially useful for tests.
We support the ListenLimit
, but it is only used in one method. Not all code paths use it. We should move it to Serve
so it is always used.
the stopLock
and chanLock
a private member. but it does not initialized any where.
when I call server.Stop(), server.stopLock is still a empty value.
srv.stopLock.Lock()
panic.
I'm using go 1.4 and graceful no longer works.
# golang.org/x/net/http2
/Users/matthew/go/src/golang.org/x/net/http2/transport.go:213: req.Cancel undefined (type *http.Request has no field or method Cancel)
/Users/matthew/go/src/golang.org/x/net/http2/transport.go:217: req.Cancel undefined (type *http.Request has no field or method Cancel)
/Users/matthew/go/src/golang.org/x/net/http2/transport.go:776: req.Cancel undefined (type *http.Request has no field or method Cancel)
The test suite fails here using Go 1.7 on Debian unstable amd64:
export GOPATH=$(mktemp -d)
go get -v github.com/tylerb/graceful
go get -v golang.org/x/net/http2
go test -v github.com/tylerb/graceful
=== RUN TestGracefulRun
--- PASS: TestGracefulRun (0.25s)
=== RUN TestGracefulRunLimitKeepAliveListener
--- PASS: TestGracefulRunLimitKeepAliveListener (0.25s)
=== RUN TestGracefulRunTimesOut
--- PASS: TestGracefulRunTimesOut (0.60s)
=== RUN TestGracefulRunDoesntTimeOut
--- PASS: TestGracefulRunDoesntTimeOut (1.00s)
=== RUN TestGracefulRunDoesntTimeOutAfterConnectionCreated
--- PASS: TestGracefulRunDoesntTimeOutAfterConnectionCreated (1.10s)
=== RUN TestGracefulRunNoRequests
--- PASS: TestGracefulRunNoRequests (0.00s)
=== RUN TestGracefulForwardsConnState
--- PASS: TestGracefulForwardsConnState (0.25s)
=== RUN TestGracefulExplicitStop
--- PASS: TestGracefulExplicitStop (0.10s)
=== RUN TestGracefulExplicitStopOverride
--- PASS: TestGracefulExplicitStopOverride (0.10s)
=== RUN TestBeforeShutdownAndShutdownInitiatedCallbacks
--- PASS: TestBeforeShutdownAndShutdownInitiatedCallbacks (0.10s)
=== RUN TestBeforeShutdownCanceled
--- PASS: TestBeforeShutdownCanceled (0.60s)
=== RUN TestNotifyClosed
--- PASS: TestNotifyClosed (0.01s)
=== RUN TestStopDeadlock
--- PASS: TestStopDeadlock (0.10s)
=== RUN TestStopRace
--- PASS: TestStopRace (0.00s)
=== RUN TestInterruptLog
--- PASS: TestInterruptLog (0.00s)
=== RUN TestMultiInterrupts
--- PASS: TestMultiInterrupts (0.00s)
=== RUN TestLogFunc
--- PASS: TestLogFunc (0.00s)
=== RUN TestHTTP2ListenAndServeTLS
--- FAIL: TestHTTP2ListenAndServeTLS (0.14s)
http2_test.go:52: Expected HTTP/2 connection to server, but connection was using HTTP/1.1
=== RUN TestHTTP2ListenAndServeTLSConfig
--- FAIL: TestHTTP2ListenAndServeTLSConfig (0.11s)
http2_test.go:52: Expected HTTP/2 connection to server, but connection was using HTTP/1.1
FAIL
exit status 1
FAIL github.com/tylerb/graceful 4.730s
0 timeout would never time out, allowing all active requests to complete.
It seem your package use/depend on the built-in log package.
While its reasonable, I propose a more flexible logging along the line of https://0value.com/about-Go-logging so the user of your package could use and choose another logging solution.
EDIT: The change is minimal, while it broke the ABI
graceful-patch.txt
TIA
I'm running go get gopkg.in/tylerb/graceful.v1
and encountering the following errors in the terminal:
../../tylerb/graceful/graceful.go:41: undefined: http.ConnState
../../tylerb/graceful/graceful.go:169: srv.Server.ConnState undefined (type *http.Server has no field or method ConnState)
../../tylerb/graceful/graceful.go:169: undefined: http.ConnState
../../tylerb/graceful/graceful.go:171: undefined: http.StateNew
../../tylerb/graceful/graceful.go:173: undefined: http.StateClosed
../../tylerb/graceful/graceful.go:173: undefined: http.StateHijacked
../../tylerb/graceful/graceful.go:271: srv.SetKeepAlivesEnabled undefined (type *Server has no field or method SetKeepAlivesEnabled)
Anything I'm doing wrong?
The readme says "If the timeout argument to Run is 0, the server never times out" but it actually exits even earlier. as soon as the active channel is not ready to receive from the default select case is chosen and simple returns from the function.
https://github.com/stretchr/graceful/blob/245127cc6e691693f813804de1fdebc67ff6044f/graceful.go#L101
// main.go
package main
import (
"log"
"net/http"
"time"
"gopkg.in/tylerb/graceful.v1"
)
func main() {
srv := &graceful.Server{
ListenLimit: 10,
TCPKeepAlive: 1 * time.Minute,
Server: &http.Server{
Handler: http.NewServeMux(),
Addr: ":8080",
},
}
log.Println(srv.ListenAndServe())
}
$ go run main.go
panic: interface conversion: net.Listener is *netutil.limitListener, not *net.TCPListener
goroutine 1 [running]:
panic(0x3184e0, 0xc82000e500)
/usr/local/Cellar/go/1.6.2/libexec/src/runtime/panic.go:481 +0x3e6
github.com/djui/mb-go/vendor/gopkg.in/tylerb/graceful%2ev1.(*Server).Serve(0xc820080780, 0x1245640, 0xc8200b6760, 0x0, 0x0)
/Users/abc/dev/go/src/github.com/djui/mb-go/vendor/gopkg.in/tylerb/graceful.v1/graceful.go:242 +0x81f
github.com/djui/mb-go/vendor/gopkg.in/tylerb/graceful%2ev1.(*Server).ListenAndServe(0xc820080780, 0x0, 0x0)
/Users/abc/dev/go/src/github.com/djui/mb-go/vendor/gopkg.in/tylerb/graceful.v1/graceful.go:151 +0xdf
main.main()
/Users/abc/dev/go/src/github.com/djui/mb-go/foo.go:20 +0x1a7
exit status 2
I am getting following error when I try to get this package using go get
go get github.com/stretchr/graceful
../github.com/stretchr/graceful/graceful.go:38: server.ConnState undefined (type *http.Server has no field or method ConnState)
../github.com/stretchr/graceful/graceful.go:38: undefined: http.ConnState
../github.com/stretchr/graceful/graceful.go:40: undefined: http.StateActive
../github.com/stretchr/graceful/graceful.go:42: undefined: http.StateClosed
../github.com/stretchr/graceful/graceful.go:42: undefined: http.StateIdle
../github.com/stretchr/graceful/graceful.go:77: server.SetKeepAlivesEnabled undefined (type *http.Server has no field or method SetKeepAlivesEnabled)
go vet
notify about problem:
graceful.go:171: assignment copies lock value to *config: crypto/tls.Config contains sync.Once contains sync.Mutex
it is also accessed in Server.shutdown
Already opened a pull request for this, my apologies, I didn't see the contribution instructions.
In the project I am working on, I was using a helper function to create and configure a server object, which was then used in calling the listen and serve functions, similar to the example in the documentation:
mux := // ...
srv := &graceful.Server{
Timeout: 10 * time.Second,
Server: &http.Server{
Addr: ":1234",
Handler: mux,
},
}
srv.ListenAndServe()
This issue I encountered is that there was no matching signature for ListenAndServeTLS - one that operated on a server object. There was only the static function that took a server as a parameter.
This made the code less consistent, and seemed like it should be in the library, so I refactored the functions to add an instance-specific one, as well as modified the static function to call it, similar to the ListenAndServe implementation, providing backwards compatibility.
Hi,
I am using graceful with Negroni:
package main
import (
// Stdlib
"flag"
"time"
// Vendor
"github.com/codegangsta/negroni"
"gopkg.in/tylerb/graceful.v1"
)
func main() {
flagAddress := flag.String("addr", ":3000", "network address")
n := negroni.Classic()
graceful.Run(*flagAddress, 10*time.Second, n)
}
It just serves index.html
from the public
folder.
Now, for some reason, when I send SIGINT
to the server, it waits for 10 seconds even though there is no active connection. Is that the desired behaviour? I was thinking that it would just exit when there are no pending requests...
$ make run
[negroni] Started GET /
[negroni] Completed 200 OK in 36.588592ms
[negroni] Started GET /favicon.ico
[negroni] Completed 0 in 33.599µs
^C
(10 seconds)
$
As per https://groups.google.com/forum/#!topic/golang-announce/eD8dh3T9yyA
Your dependency on code.google.com/p/go.net/netutil is moving to golang.org/x/net/netutil.
The test suite frequently fails using v1.2.4 with Go 1.6 on Linux x86_64 with the following output:
go test -v gopkg.in/tylerb/graceful.v1
=== RUN TestGracefulRun
--- PASS: TestGracefulRun (1.25s)
=== RUN TestGracefulRunTimesOut
--- PASS: TestGracefulRunTimesOut (2.60s)
=== RUN TestGracefulRunDoesntTimeOut
--- PASS: TestGracefulRunDoesntTimeOut (2.00s)
=== RUN TestGracefulRunNoRequests
--- PASS: TestGracefulRunNoRequests (1.00s)
=== RUN TestGracefulForwardsConnState
--- PASS: TestGracefulForwardsConnState (2.25s)
=== RUN TestGracefulExplicitStop
--- PASS: TestGracefulExplicitStop (1.10s)
=== RUN TestGracefulExplicitStopOverride
--- PASS: TestGracefulExplicitStopOverride (1.10s)
=== RUN TestBeforeShutdownAndShutdownInitiatedCallbacks
--- PASS: TestBeforeShutdownAndShutdownInitiatedCallbacks (1.10s)
=== RUN TestNotifyClosed
--- FAIL: TestNotifyClosed (0.00s)
graceful_test.go:362: listen tcp :9654: bind: address already in use
=== RUN TestStopDeadlock
--- PASS: TestStopDeadlock (1.00s)
=== RUN TestStopRace
--- PASS: TestStopRace (1.00s)
FAIL
exit status 1
FAIL gopkg.in/tylerb/graceful.v1 14.415s
The test suite sporadically fails using v1.2.4 with Go 1.6 on Linux x86_64 with the following output:
=== RUN TestGracefulRun
--- FAIL: TestGracefulRun (1.25s)
graceful_test.go:44: No response when a response was expected.
=== RUN TestGracefulRunTimesOut
--- PASS: TestGracefulRunTimesOut (2.60s)
=== RUN TestGracefulRunDoesntTimeOut
--- PASS: TestGracefulRunDoesntTimeOut (2.00s)
=== RUN TestGracefulRunNoRequests
--- PASS: TestGracefulRunNoRequests (1.00s)
=== RUN TestGracefulForwardsConnState
--- PASS: TestGracefulForwardsConnState (2.25s)
=== RUN TestGracefulExplicitStop
--- PASS: TestGracefulExplicitStop (1.10s)
=== RUN TestGracefulExplicitStopOverride
--- PASS: TestGracefulExplicitStopOverride (1.10s)
=== RUN TestBeforeShutdownAndShutdownInitiatedCallbacks
--- PASS: TestBeforeShutdownAndShutdownInitiatedCallbacks (1.10s)
=== RUN TestNotifyClosed
--- PASS: TestNotifyClosed (0.00s)
=== RUN TestStopDeadlock
--- PASS: TestStopDeadlock (1.00s)
=== RUN TestStopRace
--- PASS: TestStopRace (1.00s)
FAIL
exit status 1
FAIL gopkg.in/tylerb/graceful.v1 14.419s
I'm just trying this out and I noticed that ListenAndServe
and ListenAndServeTLS
always return an error for me when shutting down gracefully.
The error I'm seeing is:
accept tcp 127.0.0.1:1234: use of closed network connection
Thats a bit unfortunate, normally an error returned by ListenAndServe
indicates that there was a problem binding the listening socket.
Is this a known problem or is there something wrong with my setup? I'm on OSX, go 1.5.2.
Example code to reproduce:
import (
"fmt"
"net/http"
"os"
"gopkg.in/tylerb/graceful.v1"
)
func main() {
srv := &graceful.Server{
Server: &http.Server{
Addr: ":1234",
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world"))
}),
},
}
if err := srv.ListenAndServe(); err != nil {
fmt.Println("error: ", err)
os.Exit(1)
}
}
Update docs to reflect that it works with http.Handler too
@tylerb mentioned that it is probably a good time to build the next version of graceful with all the lessons learnt while maintaining the library and using it in production. This might be a complete rewrite, or we could refactor it to the next version. We are now interested in what the community would like to see in the next version as well as any feedback you may have.
For starters, these are what we care about the most:
We should close the listening socket, refusing all further requests to the server. This will allow another server to spin up and listen on that same port while the in-flight requests on the first instance finish processing.
A SIGTERM
is, along with SIGINT
, a fairly standard way to signal
to processes that they should gracefully shut down:
SIGTERM
is often used in programatic cases (e.g. by Upstart and
Heroku to signal graceful shutdown) and SIGINT
in interactive cases
(e.g. CTR-C in a terminal to stop a process). It would therefore be
useful if graceful
handled both signals, instead of just the current
SIGINT
.
What do you think about adding that SIGTERM
support ? I'd be
willing to write a code+docs patch if you were interested.
Thanks for writing and releasing graceful
!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.