I'm creating a HTTP server using Go. And whenever I work on a database maintenance, I want the server to redirect all the traffic to "current working on maintenance" page.
Currently, this is done by secret admin page (eg. http://myhome/secret) but I'm wondering if this can be done with signal -similar to TERM signal, but temporarily redirect rather than actually terminate the process.
Eg.
/home/myhome> nohup startServer &
...
/home/myhome> changeMyServerStatus "maintenance"
I assume there will be two executable.. "startServer" and "changeMyServerStatus"
So, this is similar to a service. (like reload) But, is this possible? If so, can you give me some hint please?
Thank you
As noted in the comments, signals may not be the best way to accomplish this. I’m assuming that you do want signals nonetheless.
You can use the standard user signals: SIGUSR1
to enable maintenance and SIGUSR2
to disable it.
Use os/signal
to get notified of these signals and update program state:
// Brief example code. Real code might be structured differently
// (perhaps pack up maint and http.Server in one type MyServer).
var maint uint32 // atomic: 1 if in maintenance mode
func handleMaintSignals() {
ch := make(chan os.Signal, 1)
go func() { // FIXME: use Server.RegisterOnShutdown to terminate this
for sig := range ch {
switch sig { // FIXME: add logging
case syscall.SIGUSR1:
atomic.StoreUint32(&maint, 1)
case syscall.SIGUSR2:
atomic.StoreUint32(&maint, 0)
}
}
}()
signal.Notify(ch, syscall.SIGUSR1, syscall.SIGUSR2)
}
Have a middleware look at that state and respond accordingly:
func withMaint(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if atomic.LoadUint32(&maint) == 1 {
http.Error(w, "Down for maintenance", http.StatusServiceUnavailable)
return
}
next.ServeHTTP(w, r)
})
}
You can apply this middleware on a per-route basis, or directly to the server’s root handler:
func main() {
handleMaintSignals()
srv := http.Server{
Addr: ":17990",
Handler: withMaint(http.DefaultServeMux),
}
srv.ListenAndServe()
}
You don’t need a second executable like changeMyServerStatus
. Use your operating system’s tools to send signals, such as pkill:
$ nohup myserver &
$ curl http://localhost:17990/
404 page not found
$ pkill -USR1 myserver
$ curl http://localhost:17990/
Down for maintenance
$ pkill -USR2 myserver
$ curl http://localhost:17990/
404 page not found
But manually juggling nohup
and pkill
is tedious and error-prone. Instead, use a service manager such as systemd to manage your process. Systemd lets you send arbitrary signals with systemctl kill
:
systemctl kill -s SIGUSR1 myserver