Skip to content

Commit

Permalink
feat: now gracefully shutting down the http server on SIGTERM
Browse files Browse the repository at this point in the history
  • Loading branch information
xpmatteo committed Feb 9, 2024
1 parent 74dc363 commit 2c79907
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 2 deletions.
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,21 @@
## Implementation

Almost no JavaScript! Thanks to [htmx](https://htmx.org/), we can mimic the weird ways that editing operations are
triggered in the JavaScript SPA using only the attributes of htmx. The body of the page is replaced at every request,
triggered in the JavaScript SPA using only the attributes of htmx. The body of the page is replaced at every request,
with no full page reloads.

The server is written in [Go](https://go.dev/), a language that I'm in the process of learning.

The HTML is all rendered server-side.

Interesting features
* For any request, you can receive the response in Json by using the "accept: application/json" request header
* Publishes Prometheus metrics
* Graceful shutdown
* If you request /active you will receive a full page; if you click on the corresponding button, it will just reload
the body through an ajax request


## What's missing?

Still to be done:
Expand All @@ -27,6 +35,7 @@ Still to be done:
* Clear completed
* Toggle all
* Persistence
* Make static assets cacheable by the client

## Credit

Expand Down
2 changes: 1 addition & 1 deletion server.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,5 @@ func main() {
web.GET("/js/", http.StripPrefix("/js/", http.FileServer(http.Dir("./public/js"))))

log.Println("Listening on port " + port)
log.Fatal(http.ListenAndServe(":"+port, nil))
web.GracefulListenAndServe(":"+port, nil)
}
43 changes: 43 additions & 0 deletions web/graceful.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package web

import (
"context"
"errors"
"log"
"net/http"
"os"
"os/signal"
)

// GracefulListenAndServe will ensure that when the server receives a SIGTERM signal,
// it will shut down gracefully. It will wait for all connections, both open and idle
// to be closed before shutting down the process.
//
// There is no timeout enforced, because it is the job of the container to do that.
// For instance, Kubernetes will eventually forcefully kill a pod after waiting
// for a configured timeout for it to exit cleanly.
func GracefulListenAndServe(addr string, handler http.Handler) {
server := &http.Server{Addr: addr, Handler: handler}

idleConnsClosed := make(chan struct{})
go func() {
sigint := make(chan os.Signal, 1)
signal.Notify(sigint, os.Interrupt)
<-sigint

// We received an interrupt signal, shut down.
if err := server.Shutdown(context.Background()); err != nil {
// Error from closing listeners, or context timeout:
log.Printf("HTTP server Shutdown: %v", err)
}
close(idleConnsClosed)
}()

err := server.ListenAndServe()
if !errors.Is(err, http.ErrServerClosed) {
// Error starting or closing listener
log.Fatal(err)
}

<-idleConnsClosed
}

0 comments on commit 2c79907

Please sign in to comment.