From 96f2a59cc71a800680fd3a0a9d93bd13d841c005 Mon Sep 17 00:00:00 2001 From: Saalik Mubeen Date: Tue, 4 Jun 2024 13:37:27 +0530 Subject: [PATCH] added graceful shutdown of the server --- cmd/goravel/helpers.go | 10 ++++-- cmd/goravel/templates/new/go.mod.txt | 2 +- cmd/goravel/templates/new/main.go.txt | 52 ++++++++++++++++++++++++++- goravel.go | 34 +----------------- server.go | 44 +++++++++++++++++++++++ 5 files changed, 105 insertions(+), 37 deletions(-) create mode 100644 server.go diff --git a/cmd/goravel/helpers.go b/cmd/goravel/helpers.go index 663c6c4..6aeaf31 100644 --- a/cmd/goravel/helpers.go +++ b/cmd/goravel/helpers.go @@ -12,14 +12,20 @@ func showHelp() { help - show the help commands version - print application version + serve - starts the server + new - creates a new goravel application + make migration - creates two new migration files one up and one down in the migrations folder migrate - runs all up migrations that have not been run previously + migrate up - same as migrate migrate down - reverses the most recent migration + migrate down all - reverses all migrations + migrate to - migrates to a specific migration number (number: positive for up, negative for down migrations) + migrate fix - Fix the migrations table if it's corrupted by forcing the version to the last migration (-1) migrate reset - runs all down migrations in reverse order, and then all up migrations - make migration - creates two new migration files one up and one down in the migrations folder make auth - creates and runs migrations for authentication tables, and creates models and middleware + make session - creates a table in the database as a session store make handler - creates a stub handler in the handlers directory make model - creates a new model in the data directory - make session - creates a table in the database as a session store `) } diff --git a/cmd/goravel/templates/new/go.mod.txt b/cmd/goravel/templates/new/go.mod.txt index 6ac7052..0719ea3 100644 --- a/cmd/goravel/templates/new/go.mod.txt +++ b/cmd/goravel/templates/new/go.mod.txt @@ -3,7 +3,7 @@ module ${APP_URL} go 1.22.3 require ( - github.com/saalikmubeen/goravel v1.4.2 + github.com/saalikmubeen/goravel v1.5.0 github.com/CloudyKit/jet/v6 v6.2.0 github.com/go-chi/chi/v5 v5.0.12 github.com/upper/db/v4 v4.7.0 diff --git a/cmd/goravel/templates/new/main.go.txt b/cmd/goravel/templates/new/main.go.txt index 55a73a6..903c8e1 100644 --- a/cmd/goravel/templates/new/main.go.txt +++ b/cmd/goravel/templates/new/main.go.txt @@ -1,6 +1,11 @@ package main import ( + "os" + "os/signal" + "sync" + "syscall" + "github.com/saalikmubeen/goravel" "${APP_URL}/handlers" "${APP_URL}/middleware" @@ -12,11 +17,56 @@ type application struct { Handlers *handlers.Handlers Models *models.Models Middleware *middleware.Middleware + wg *sync.WaitGroup } + + + func main() { app := initGoravel() - app.App.ListenAndServe() + + go app.listenForShutDown() + err := app.App.ListenAndServe() + if err != nil { + app.App.ErrorLog.Println(err) + } + +} + +func (app *application) listenForShutDown() { + // Create a quit channel which carries os.Signal values. Use buffered + // We need to use a buffered channel here because signal.Notify() does not + // wait for a receiver to be available when sending a signal to the quit channel. + // If we had used a regular (non-buffered) channel here instead, a signal could be + // ‘missed’ if our quit channel is not ready to receive at the exact moment that the + // signal is sent. By using a buffered channel, we avoid this problem and ensure + // that we never miss a signal. + quit := make(chan os.Signal, 1) + + // Use signal.Notify() to listen for incoming SIGINT and SIGTERM signals and relay + // them to the quit channel. Any other signal will not be caught by signal.Notify() + // and will retain their default behavior. + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + + // Read the signal from the quit channel. This code will block until a signal is + // received. + s := <-quit + + // Log a message to say we caught the signal. Notice that we also call the + // String() method on the signal to get the signal name and include it in the log + // entry properties. + app.App.InfoLog.Printf("caught signal: %s", s.String()) + + // ** put any clean up tasks here + + // ** + + // Call Wait() to block until our WaitGroup counter is zero. This essentially blocks + // until the background goroutines have finished. Then we return nil on the shutdownError + // channel to indicate that the shutdown as compleeted without any issues. + // Uses sync.WaitGroup to wait for any background goroutines before terminating the application. + app.wg.Wait() } diff --git a/goravel.go b/goravel.go index 0b2bc18..c266c22 100644 --- a/goravel.go +++ b/goravel.go @@ -3,7 +3,6 @@ package goravel import ( "fmt" "log" - "net/http" "os" "strconv" "strings" @@ -12,7 +11,6 @@ import ( "github.com/CloudyKit/jet/v6" "github.com/alexedwards/scs/v2" "github.com/dgraph-io/badger/v3" - "github.com/fatih/color" "github.com/go-chi/chi/v5" "github.com/gomodule/redigo/redis" "github.com/joho/godotenv" @@ -25,7 +23,7 @@ import ( const ( // Version of Goravel - Version = "1.4.2" + Version = "1.5.0" // http://patorjk.com/software/taag/#p=display&f=Ogre&t=Goravel Banner = ` ___ _ @@ -379,33 +377,3 @@ func (g *Goravel) New(rootPath string) error { return nil } - -// ListenAndServe starts the web server -func (g *Goravel) ListenAndServe() { - port := os.Getenv("PORT") - srv := &http.Server{ - Addr: ":" + port, - Handler: g.Routes, - ErrorLog: g.ErrorLog, - IdleTimeout: time.Second * 30, - ReadTimeout: time.Second * 30, - WriteTimeout: time.Second * 600, - } - - if g.DB.Pool != nil { - defer g.DB.Pool.Close() // close the database connection when the server stops - } - - if redisPool != nil { - defer redisPool.Close() // close the redis connection when the server stops - } - - if badgerConn != nil { - defer badgerConn.Close() - } - - color.Yellow(Banner, Version) - color.Green("Starting server on port %s", port) - err := srv.ListenAndServe() - g.ErrorLog.Fatal(err) -} diff --git a/server.go b/server.go new file mode 100644 index 0000000..29963fe --- /dev/null +++ b/server.go @@ -0,0 +1,44 @@ +package goravel + +import ( + "net/http" + "os" + "time" + + "github.com/fatih/color" +) + +// ListenAndServe starts the web server +func (g *Goravel) ListenAndServe() error { + port := os.Getenv("PORT") + srv := &http.Server{ + Addr: ":" + port, + Handler: g.Routes, + ErrorLog: g.ErrorLog, + IdleTimeout: time.Second * 30, + ReadTimeout: time.Second * 30, + WriteTimeout: time.Second * 600, + } + + if g.DB.Pool != nil { + defer g.DB.Pool.Close() // close the database connection when the server stops + } + + if redisPool != nil { + defer redisPool.Close() // close the redis connection when the server stops + } + + if badgerConn != nil { + defer badgerConn.Close() + } + + color.Yellow(Banner, Version) + color.Green("Starting server on port %s", port) + err := srv.ListenAndServe() + + if err != nil { + return err + } else { + return nil + } +}