

zzh@ZZHPC:/zdata/Github/greenlight$ go run ./cmd/api time=2024-11-18T19:49:43.864+08:00 level=INFO msg="database connection pool established" time=2024-11-18T19:49:43.864+08:00 level=INFO msg="starting server" addr=:4000 env=development
Doing this should start a process with the name api on your machine. You can use the pgrep command to verify that this process exists, like so:
zzh@ZZHPC:~$ pgrep -l api 25711 api
Once that’s confirmed, go ahead and try sending a SIGKILL signal to the api process using the pkill command like so:
zzh@ZZHPC:~$ ps -ef | grep 25711 zzh 25711 25576 0 19:49 pts/0 00:00:00 /tmp/go-build3971774478/b001/exe/api zzh 25976 9327 0 19:53 pts/1 00:00:00 grep --color=auto 25711 zzh@ZZHPC:~$ pkill -SIGKILL api

zzh@ZZHPC:/zdata/Github/greenlight$ go run ./cmd/api time=2024-11-18T19:49:43.864+08:00 level=INFO msg="database connection pool established" time=2024-11-18T19:49:43.864+08:00 level=INFO msg="starting server" addr=:4000 env=development signal: killed
Feel free to repeat the same process, but sending a SIGTERM signal instead:
zzh@ZZHPC:~$ pgrep -l api 26248 api zzh@ZZHPC:~$ pkill -SIGTERM api
zzh@ZZHPC:/zdata/Github/greenlight$ go run ./cmd/api time=2024-11-18T19:56:08.698+08:00 level=INFO msg="database connection pool established" time=2024-11-18T19:56:08.698+08:00 level=INFO msg="starting server" addr=:4000 env=development signal: terminated

zzh@ZZHPC:~$ pgrep -l api 26488 api zzh@ZZHPC:~$ pkill -SIGQUIT api
zzh@ZZHPC:/zdata/Github/greenlight$ go run ./cmd/api
time=2024-11-18T19:58:36.775+08:00 level=INFO msg="database connection pool established"
time=2024-11-18T19:58:36.775+08:00 level=INFO msg="starting server" addr=:4000 env=development
SIGQUIT: quit
PC=0x476461 m=0 sigcode=0
goroutine 0 gp=0xa18580 m=0 mp=0xa191e0 [idle]:
runtime.futex(0xa19320, 0x80, 0x0, 0x0, 0x0, 0x0)
/home/zzh/.goenv/versions/1.23.0/src/runtime/sys_linux_amd64.s:557 +0x21 fp=0x7ffee822bb60 sp=0x7ffee822bb58 pc=0x476461
runtime.futexsleep(0x7ffee822bbd8?, 0x40da90?, 0x1e822bc08?)
/home/zzh/.goenv/versions/1.23.0/src/runtime/os_linux.go:69 +0x30 fp=0x7ffee822bbb0 sp=0x7ffee822bb60 pc=0x432c10
runtime.notesleep(0xa19320)
/home/zzh/.goenv/versions/1.23.0/src/runtime/lock_futex.go:170 +0x87 fp=0x7ffee822bbe8 sp=0x7ffee822bbb0 pc=0x40dc27
runtime.mPark(...)
/home/zzh/.goenv/versions/1.23.0/src/runtime/proc.go:1866
runtime.stopm()
/home/zzh/.goenv/versions/1.23.0/src/runtime/proc.go:2885 +0x8c fp=0x7ffee822bc18 sp=0x7ffee822bbe8 pc=0x43e16c
runtime.findRunnable()
/home/zzh/.goenv/versions/1.23.0/src/runtime/proc.go:3622 +0xd5c fp=0x7ffee822bd90 sp=0x7ffee822bc18 pc=0x43fbdc
runtime.schedule()
/home/zzh/.goenv/versions/1.23.0/src/runtime/proc.go:3995 +0xb1 fp=0x7ffee822bdc8 sp=0x7ffee822bd90 pc=0x440cb1
runtime.park_m(0xc0000061c0)
/home/zzh/.goenv/versions/1.23.0/src/runtime/proc.go:4102 +0x1eb fp=0x7ffee822be20 sp=0x7ffee822bdc8 pc=0x4410cb
runtime.mcall()
/home/zzh/.goenv/versions/1.23.0/src/runtime/asm_amd64.s:459 +0x4e fp=0x7ffee822be38 sp=0x7ffee822be20 pc=0x47266e
goroutine 1 gp=0xc0000061c0 m=nil [IO wait]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
/home/zzh/.goenv/versions/1.23.0/src/runtime/proc.go:424 +0xce fp=0xc00014f9c8 sp=0xc00014f9a8 pc=0x46cbce
runtime.netpollblock(0x10?, 0x405be6?, 0x0?)
/home/zzh/.goenv/versions/1.23.0/src/runtime/netpoll.go:575 +0xf7 fp=0xc00014fa00 sp=0xc00014f9c8 pc=0x431eb7
internal/poll.runtime_pollWait(0x78b3a509d6a0, 0x72)
/home/zzh/.goenv/versions/1.23.0/src/runtime/netpoll.go:351 +0x85 fp=0xc00014fa20 sp=0xc00014fa00 pc=0x46bec5
internal/poll.(*pollDesc).wait(0xc000130400?, 0x10?, 0x0)
/home/zzh/.goenv/versions/1.23.0/src/internal/poll/fd_poll_runtime.go:84 +0x27 fp=0xc00014fa48 sp=0xc00014fa20 pc=0x4c3c07
internal/poll.(*pollDesc).waitRead(...)
/home/zzh/.goenv/versions/1.23.0/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Accept(0xc000130400)
...
exit status 2


package main import ( "fmt" "log/slog" "net/http" "time" ) func (app *application) serve() error { srv := &http.Server{ Addr: fmt.Sprintf(":%d", app.config.port), Handler: app.routes(), IdleTimeout: time.Minute, ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, ErrorLog: slog.NewLogLogger(app.logger.Handler(), slog.LevelError), } app.logger.Info("starting server", "addr", srv.Addr, "env", app.config.env) return srv.ListenAndServe() }
With that in place, we can simplify our main() function to use this new app.serve() method like so:
... // Call app.serve() to start the server. err = app.serve() if err != nil { logger.Error(err.Error()) os.Exit(1) } ...

func (app *application) serve() error { srv := &http.Server{ Addr: fmt.Sprintf(":%d", app.config.port), Handler: app.routes(), IdleTimeout: time.Minute, ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, ErrorLog: slog.NewLogLogger(app.logger.Handler(), slog.LevelError), } // Start a background goroutine to catch signals. go func() { quit := make(chan os.Signal, 1) // Use signal.Notify() to listen for incoming SIGINT and SIGTERM signals and // relay them to the quit channel. 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 app.logger.Info("caught signal", "signal", s.String()) os.Exit(0) }() app.logger.Info("starting server", "addr", srv.Addr, "env", app.config.env) return srv.ListenAndServe() }


zzh@ZZHPC:/zdata/Github/greenlight$ go run ./cmd/api time=2024-11-18T20:35:36.803+08:00 level=INFO msg="database connection pool established" time=2024-11-18T20:35:36.803+08:00 level=INFO msg="starting server" addr=:4000 env=development ^Ctime=2024-11-18T20:35:38.712+08:00 level=INFO msg="caught signal" signal=interrupt

zzh@ZZHPC:~$ pkill -SIGTERM api
zzh@ZZHPC:/zdata/Github/greenlight$ go run ./cmd/api time=2024-11-18T20:36:52.920+08:00 level=INFO msg="database connection pool established" time=2024-11-18T20:36:52.920+08:00 level=INFO msg="starting server" addr=:4000 env=development time=2024-11-18T20:37:16.602+08:00 level=INFO msg="caught signal" signal=terminated

func (app *application) serve() error { srv := &http.Server{ Addr: fmt.Sprintf(":%d", app.config.port), Handler: app.routes(), IdleTimeout: time.Minute, ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, ErrorLog: slog.NewLogLogger(app.logger.Handler(), slog.LevelError), } // The shutdownError channel is used to receive any errors returned by the // graceful Shutdown() function. shutdownError := make(chan error) // Start a background goroutine to catch signals. go func() { quit := make(chan os.Signal, 1) // Use signal.Notify() to listen for incoming SIGINT and SIGTERM signals and // relay them to the quit channel. 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 app.logger.Info("shutting down server", "signal", s.String()) ctx, cancel := context.WithTimeout(context.Background(), 30 * time.Second) defer cancel() shutdownError <- srv.Shutdown(ctx) }() app.logger.Info("starting server", "addr", srv.Addr, "env", app.config.env) err := srv.ListenAndServe() if !errors.Is(err, http.ErrServerClosed) { return err } err = <-shutdownError if err != nil { return err } app.logger.Info("stopped server", "addr", srv.Addr) return nil }

func (app *application) healthcheckHandler(w http.ResponseWriter, r *http.Request) { data := envelope{ "status": "available", "system_info": map[string]string{ "environment": app.config.env, "version": version, }, } // Add a 4 second delay. time.Sleep(4 * time.Second) err := app.writeJSON(w, http.StatusOK, data, nil) if err != nil { app.serverErrorResponse(w, r, err) } }

zzh@ZZHPC:~$ curl localhost:4000/v1/healthcheck & pkill -SIGTERM api
[1] 33833 (此行命令执行后立即出现)
zzh@ZZHPC:~$ {
"status": "available",
"system_info": {
"environment": "development",
"version": "1.0.0"
}
}
(以上输出在命令执行后4秒出现。光标停在此,好几分钟后按回车键才出现下面一行)
[1]+ Done curl localhost:4000/v1/healthcheck
zzh@ZZHPC:~$


zzh@ZZHPC:/zdata/Github/greenlight$ go run ./cmd/api time=2024-11-18T21:27:01.271+08:00 level=INFO msg="database connection pool established" time=2024-11-18T21:27:01.271+08:00 level=INFO msg="starting server" addr=:4000 env=development time=2024-11-18T21:27:02.114+08:00 level=INFO msg="shutting down server" signal=terminated(此行在 curl & pkill 命令执行后立即出现,因为该日志是在srv.Shutdown(ctx)之前) time=2024-11-18T21:27:06.295+08:00 level=INFO msg="stopped server" addr=:4000 (此行在 curl & pkill 命令执行后4秒出现)


浙公网安备 33010602011771号