From 06a6c80433c23b58ee0d76fefd5fcb498fa257ea Mon Sep 17 00:00:00 2001 From: Edwar Baron Date: Mon, 8 Jun 2020 11:08:08 -0500 Subject: [PATCH] [improvement/server-graceful-shutdown] Add support of graceful shutdown (#312) --- Dockerfile | 2 +- README.md | 10 ++++++++++ imaginary.go | 5 +---- server.go | 28 ++++++++++++++++++++++++++-- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index d516c3c..164b436 100644 --- a/Dockerfile +++ b/Dockerfile @@ -78,7 +78,7 @@ COPY --from=builder /etc/ssl/certs /etc/ssl/certs RUN DEBIAN_FRONTEND=noninteractive \ apt-get update && \ apt-get install --no-install-recommends -y \ - libglib2.0-0 libjpeg62-turbo libpng16-16 libopenexr23 \ + procps libglib2.0-0 libjpeg62-turbo libpng16-16 libopenexr23 \ libwebp6 libwebpmux3 libwebpdemux2 libtiff5 libgif7 libexif12 libxml2 libpoppler-glib8 \ libmagickwand-6.q16-6 libpango1.0-0 libmatio4 libopenslide0 \ libgsf-1-114 fftw3 liborc-0.4-0 librsvg2-2 libcfitsio7 libimagequant0 libheif1 && \ diff --git a/README.md b/README.md index 6c35563..0b04b20 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,16 @@ You can enable it simply passing a flag to the binary: $ imaginary -concurrency 20 ``` +### Graceful shutdown + +When you use a cluster, it is necessary to control how the deployment is executed, and it is very useful to finish the containers in a controlled. + +You can use the next command: + +``` +$ ps auxw | grep 'bin/imaginary' | awk 'NR>1{print buf}{buf = $2}' | xargs kill -TERM > /dev/null 2>&1 +``` + ### Scalability If you're looking for a large scale solution for massive image processing, you should scale `imaginary` horizontally, distributing the HTTP load across a pool of imaginary servers. diff --git a/imaginary.go b/imaginary.go index 0b3e9e0..5f8a2ee 100644 --- a/imaginary.go +++ b/imaginary.go @@ -219,10 +219,7 @@ func main() { LoadSources(opts) // Start the server - err := Server(opts) - if err != nil { - exitWithError("cannot start the server: %s", err) - } + Server(opts) } func getPort(port int) int { diff --git a/server.go b/server.go index 9792a0e..90c5eb9 100644 --- a/server.go +++ b/server.go @@ -1,9 +1,13 @@ package main import ( + "context" "net/http" "net/url" + "log" "os" + "os/signal" + "syscall" "path" "strconv" "strings" @@ -56,7 +60,7 @@ func (e Endpoints) IsValid(r *http.Request) bool { return true } -func Server(o ServerOptions) error { +func Server(o ServerOptions) { addr := o.Address + ":" + strconv.Itoa(o.Port) handler := NewLog(NewServerMux(o), os.Stdout, o.LogLevel) @@ -68,7 +72,27 @@ func Server(o ServerOptions) error { WriteTimeout: time.Duration(o.HTTPWriteTimeout) * time.Second, } - return listenAndServe(server, o) + done := make(chan os.Signal, 1) + signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + go func() { + if err := listenAndServe(server, o); err != nil && err != http.ErrServerClosed { + log.Fatalf("listen: %s\n", err) + } + }() + + <-done + log.Print("Graceful shutdown") + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer func() { + // extra handling here + cancel() + }() + + if err := server.Shutdown(ctx); err != nil { + log.Fatalf("Server Shutdown Failed:%+v", err) + } } func listenAndServe(s *http.Server, o ServerOptions) error {