Gin Practice Serialized Seven Golang Gracefully Restart HTTP Service

  Elegant, golang, Hot update, http

Elegant restart service

When writing the case code earlier, I believe you will think of

Every time the code is updated, after the configuration file is updated
Just like thatctrl+cIs it really okay?ctrl+cWhat have you done?

In this section we will briefly talk aboutctrl+cBehindSignalAnd how to do itGinInElegant restart service, that is, rightHTTPHot update of services

Project address:https://github.com/EDDYCJY/go …

ctrl + c

The kernel sends signals under certain circumstances, such as when a process writes data to a closed pipeline.SIGPIPESignal

Executing a specific key combination at the terminal can cause the system to send a specific signal to this process to complete a series of actions.

Command Signal Meaning
ctrl + c SIGINT Force process to end
ctrl + z SIGTSTP Task Interrupted, Process Suspended
ctrl + \ SIGQUIT End of process anddump core
ctrl + d EOF
SIGHUP Terminate the process of receiving the signal. If the signal is not captured in the program, the process will exit when the signal is received (usually used to restart and reload the process)

So in our implementationctrl + cCloseginOn the server side,Will force the process to end, causing problems for users who are accessing, etc.

commonkill -9 pidWill sendSIGKILLSignals to the process are similar results.

Signal

This paragraph appears repeatedly.SignalWhat is it?

The signal isUnixClassUnixAnd othersPOSIXA restricted mode of interprocess communication in compatible operating systems.

It is an asynchronous notification mechanism to remind a process that an event (hardware exception, program execution exception, external signal) has occurred. When a signal is sent to a process, the operating system interrupts the normal control flow of the process. At this point, any non-atomic operation will be interrupted. If the process defines a signal processing function, it will be executed, otherwise the default processing function will be executed.

All signals

$ kill -l
 1) SIGHUP     2) SIGINT     3) SIGQUIT     4) SIGILL     5) SIGTRAP
 6) SIGABRT     7) SIGBUS     8) SIGFPE     9) SIGKILL    10) SIGUSR1
11) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM
16) SIGSTKFLT    17) SIGCHLD    18) SIGCONT    19) SIGSTOP    20) SIGTSTP
21) SIGTTIN    22) SIGTTOU    23) SIGURG    24) SIGXCPU    25) SIGXFSZ
26) SIGVTALRM    27) SIGPROF    28) SIGWINCH    29) SIGIO    30) SIGPWR
31) SIGSYS    34) SIGRTMIN    35) SIGRTMIN+1    36) SIGRTMIN+2    37) SIGRTMIN+3
38) SIGRTMIN+4    39) SIGRTMIN+5    40) SIGRTMIN+6    41) SIGRTMIN+7    42) SIGRTMIN+8
43) SIGRTMIN+9    44) SIGRTMIN+10    45) SIGRTMIN+11    46) SIGRTMIN+12    47) SIGRTMIN+13
48) SIGRTMIN+14    49) SIGRTMIN+15    50) SIGRTMAX-14    51) SIGRTMAX-13    52) SIGRTMAX-12
53) SIGRTMAX-11    54) SIGRTMAX-10    55) SIGRTMAX-9    56) SIGRTMAX-8    57) SIGRTMAX-7
58) SIGRTMAX-6    59) SIGRTMAX-5    60) SIGRTMAX-4    61) SIGRTMAX-3    62) SIGRTMAX-2
63) SIGRTMAX-1    64) SIGRTMAX

How to calculate elegance

Purpose

  • Do not close existing connections (running programs)
  • The new process starts and replaces the old process.
  • The new process takes over the new connection
  • The connection should respond to the user’s request at any time. When the user is still requesting the old process, the connection should be maintained. The new user should request the new process and the request cannot be rejected.

Process

1, replace the executable file or modify the configuration file

2. Sending semaphoresSIGHUP

3. Reject the old process of the new connection request, but ensure that the existing connection is normal.

4. Start a new subprocess

5. The new subprocess startsAccet

6. The system transfers the new request to the new subprocess.

7. The old process ends normally after processing all the old connections.

Realize graceful restart

endless

Zero downtime restarts for golang HTTP and HTTPS servers. (for golang 1.3+)

We usefvbock/endlessTo achieveGolang HTTP/HTTPSZero downtime for service restart

endless serverMonitor the following semaphores:

  • Sighup: triggerforkSub-processes and Restart
  • Sigusr1/syscall.sigtstp: monitored but no action triggered
  • Sigusr2: triggerhammerTime
  • Syscall.SIGINT/syscall.SIGTERM: Triggers server shutdown (completes running request)

endlessIt is precisely by monitoring thesesemaphoreTo complete a series of control actions

Installation

go get -u github.com/fvbock/endless

write

Opengin-blogThemain.goFile, modify file:

package main

import (
    "fmt"
    "log"
    "syscall"

    "github.com/fvbock/endless"

    "gin-blog/routers"
    "gin-blog/pkg/setting"
)

func main() {
    endless.DefaultReadTimeOut = setting.ReadTimeout
    endless.DefaultWriteTimeOut = setting.WriteTimeout
    endless.DefaultMaxHeaderBytes = 1 << 20
    endPoint := fmt.Sprintf(":%d", setting.HTTPPort)

    server := endless.NewServer(endPoint, routers.InitRouter())
    server.BeforeBegin = func(add string) {
        log.Printf("Actual pid is %d", syscall.Getpid())
    }

    err := server.ListenAndServe()
    if err != nil {
        log.Printf("Server err: %v", err)
    }
}

endless.NewServerReturns an initializedendlessServerObject, inBeforeBeginThe of the current process is output when thepid, callingListenAndServeThe service will actually be “started.”

Verification

Compile
$ go build main.go 
carry out
$ ./main
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
...
Actual pid is 48601

After successful startup, outputpid48601; Executed at another terminalkill -1 48601, check the terminal effect of the previous service

[root@localhost go-gin-example]# ./main
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:    export GIN_MODE=release
 - using code:    gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /auth                     --> ...
[GIN-debug] GET    /api/v1/tags              --> ...
...

Actual pid is 48601

...

Actual pid is 48755
48601 Received SIGTERM.
48601 [::]:8000 Listener closed.
48601 Waiting for connections to finish...
48601 Serve() returning...
Server err: accept tcp [::]:8000: use of closed network connection

You can see that the command has been suspended andforkThe new subprocesspidFor48755

48601 Received SIGTERM.
48601 [::]:8000 Listener closed.
48601 Waiting for connections to finish...
48601 Serve() returning...
Server err: accept tcp [::]:8000: use of closed network connection

The main process (pid48601) receivedSIGTERMSemaphore, turn off the monitoring of the main process and wait for the executing request to complete; This is consistent with our previous description.

awaken

At this time inpostmanVisit our interface again, you can be pleasantly surprised to find that he is “resurrected”!

Actual pid is 48755
48601 Received SIGTERM.
48601 [::]:8000 Listener closed.
48601 Waiting for connections to finish...
48601 Serve() returning...
Server err: accept tcp [::]:8000: use of closed network connection


$ [GIN] 2018/03/15 - 13:00:16 | 200 |     188.096µs |   192.168.111.1 | GET      /api/v1/tags...

This completes a positive flow

Think about it, every time an update is published or a configuration file is modified, it only needs to be sent to the process.SIGTERM signal, without forcing the end of the application, how convenient and safe it is!

Problem

endlessHot update is a way to exit the original process after creating the subprocess, which does not meet the requirements of the daemon process.

http.Server – Shutdown()

If yoursGolang >= 1.8, you can also consider usinghttp.ServerTheShutdownMethod

package main

import (
    "fmt"
    "net/http"
    "context"
    "log"
    "os"
    "os/signal"
    "time"


    "gin-blog/routers"
    "gin-blog/pkg/setting"
)

func main() {
    router := routers.InitRouter()

    s := &http.Server{
        Addr:           fmt.Sprintf(":%d", setting.HTTPPort),
        Handler:        router,
        ReadTimeout:    setting.ReadTimeout,
        WriteTimeout:   setting.WriteTimeout,
        MaxHeaderBytes: 1 << 20,
    }

    go func() {
        if err := s.ListenAndServe(); err != nil {
            log.Printf("Listen: %s\n", err)
        }
    }()
    
    quit := make(chan os.Signal)
    signal.Notify(quit, os.Interrupt)
    <- quit

    log.Println("Shutdown Server ...")

    ctx, cancel := context.WithTimeout(context.Background(), 5 * time.Second)
    defer cancel()
    if err := s.Shutdown(ctx); err != nil {
        log.Fatal("Server Shutdown:", err)
    }

    log.Println("Server exiting")
}

Summary

Elegant restart (hot update) is a very important part of daily service. AndGolangInHTTPThere are also many plans for hot service updates. We should choose the most suitable one according to the actual application scenarios.

References

This series of sample codes

This series of catalogues

Expand reading