gin 优雅退出
版权声明 本站原创文章 由 萌叔 发表
转载请注明 萌叔 | https://vearne.cc
前言
gin底层使用的是net/http, 所以gin的优雅退出就等于
http.Server的优雅退出, Golang 1.8以后提供了Shutdown函数,可以优雅关闭http.Server
func (srv *Server) Shutdown(ctx context.Context) error
优雅退出的过程
- 1)关闭所有的监听
- 2)后关闭所有的空闲连接
- 3)无限期等待活动的连接处理完毕转为空闲,并关闭
如果提供了带有超时的Context,将在服务关闭前返回 Context的超时错误
完整的例子
package main
import (
"net/http"
"time"
"os"
"os/signal"
"syscall"
"fmt"
"github.com/gin-gonic/gin"
"github.com/pkg/errors"
)
func SlowHandler(c *gin.Context) {
fmt.Println("[start] SlowHandler")
//time.Sleep(30 * time.Second)
time.Sleep(30 * time.Second)
fmt.Println("[end] SlowHandler")
c.JSON(http.StatusOK, gin.H{
"message": "success",
})
}
// 实现context.Context接口
type ExitContext struct{
Chan chan struct{}
DeadLineTime time.Time
}
func NewExitContext(duration time.Duration) *ExitContext{
cxt := ExitContext{}
cxt.DeadLineTime = time.Now().Add(duration)
cxt.Chan = make(chan struct{}, 1)
return &cxt
}
func (cxt *ExitContext) Done() <-chan struct{}{
if time.Now().After(cxt.DeadLineTime){
cxt.Chan <- struct{}{}
}
return cxt.Chan
}
func (cxt *ExitContext) Err() error{
return errors.New("can't exit before Specified time")
}
// 无意义的空函数
func(cxt *ExitContext) Value(key interface{}) interface{}{
return nil
}
func(ctx *ExitContext) Deadline() (deadline time.Time, ok bool){
deadline = ctx.DeadLineTime
ok = true
return
}
func main() {
r := gin.Default()
// 1.
r.GET("/slow", SlowHandler)
server := &http.Server{
Addr: ":8080",
Handler: r,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
go server.ListenAndServe()
// 设置优雅退出
gracefulExitWeb(server)
}
func gracefulExitWeb(server *http.Server) {
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT)
sig := <-ch
fmt.Println("got a signal", sig)
now := time.Now()
cxt := NewExitContext(3*time.Second)
err := server.Shutdown(cxt)
if err != nil{
fmt.Println("err", err)
}
// 看看实际退出所耗费的时间
fmt.Println("------exited--------", time.Since(now))
}
更简单的写法
package main
import (
"net/http"
"time"
"os"
"os/signal"
"syscall"
"fmt"
"github.com/gin-gonic/gin"
"context"
)
func SlowHandler(c *gin.Context) {
fmt.Println("[start] SlowHandler")
//time.Sleep(30 * time.Second)
time.Sleep(30 * time.Second)
fmt.Println("[end] SlowHandler")
c.JSON(http.StatusOK, gin.H{
"message": "success",
})
}
func main() {
r := gin.Default()
// 1.
r.GET("/slow", SlowHandler)
server := &http.Server{
Addr: ":8080",
Handler: r,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
go server.ListenAndServe()
// 设置优雅退出
gracefulExitWeb(server)
}
func gracefulExitWeb(server *http.Server) {
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT)
sig := <-ch
fmt.Println("got a signal", sig)
now := time.Now()
cxt, cancel := context.WithTimeout(context.Background(), 5 * time.Second)
defer cancel()
err := server.Shutdown(cxt)
if err != nil{
fmt.Println("err", err)
}
// 看看实际退出所耗费的时间
fmt.Println("------exited--------", time.Since(now))
}
参考资料1中的例子有错误
如果context 被设置为nil,程序实际是异常崩溃了
log.Println(s.Shutdown(nil))
参考资料
1.Go 1.8 http graceful 体验
2.Gin实践 连载七 Golang优雅重启HTTP服务
后记
萌叔写了一个方便管理多个worker,轻松启动停止的库,欢迎使用
vearne/worker_manager
go server.ListenAndServe()
请问这里的服务为什么放在协程中呢,刚入门go,看到好多地方都这样写,但没搞清楚为什么
server.ListenAndServe()
本身会监听在socket上,如果不放到另一个协程中,
后面的代码
是无法被执行到的