gin 优雅退出

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://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)) } 更简单的写法 ...

September 14, 2018 · 2 min

golang fasthttp优雅退出

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 引言 使用Golang进行开发服务,我们会特别关心性能,fasthttp是标准库net/http性能的10倍。所以我们的web服务大量使用它。不过这个库并没有提供优雅退出方案, 查看了很多资料,我找到一个解决方案。 1.原理 对于web服务而言,优雅退出其实分为2步 1)关闭监听的端口,不再接受新的请求 2)对于正在处理的请求等待它们处理完成,如果全部处理完成,或者默认的超时时间已达,则退出 2. 实现 要达到这个目标需要实现 type Listener interface 下面展示一部分核心代码 grace_listener.go type GracefulListener struct { // inner listener ln net.Listener // maximum wait time for graceful shutdown maxWaitTime time.Duration // this channel is closed during graceful shutdown on zero open connections. done chan struct{} // the number of open connections connsCount uint64 // becomes non-zero when graceful shutdown starts shutdown uint64 } // Close closes the inner listener and waits until all the pending open connections // are closed before returning. func (ln *GracefulListener) Close() error { // 不再接受新的请求 err := ln.ln.Close() if err != nil { return nil } // 等待已经接到的请求处理完成 return ln.waitForZeroConns() } func (ln *GracefulListener) waitForZeroConns() error { atomic.AddUint64(&ln.shutdown, 1) fmt.Println("waitForZeroConns", atomic.LoadUint64(&ln.connsCount)) if atomic.LoadUint64(&ln.connsCount) == 0 { close(ln.done) return nil } select { case <-ln.done: return nil case <-time.After(ln.maxWaitTime): return fmt.Errorf("cannot complete graceful shutdown in %s", ln.maxWaitTime) } return nil } server.go 初始化&启动web服务 ...

January 18, 2018 · 1 min

优雅的终止docker容器

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 起因: 本文受到参考资料1的启发 我们线上的服务有不少都是部署在docker中,部署涉及的机器多大几十台, 服务发布时,要求前一个版本的容器必须优雅的退出。 docker容器中的进程是一个任务消费者。不断得从任务队列中取任务,然后进行执行(执行时间较长) 假定docker容器的name为test_v1 docker容器中的进程名为atm 也就是说不能简单的 docker rm -vf test_v1 docker run -d --name test_v1 test:v1 解决方案 1)方案1 首先,我想到的办法是在docker容器外部使用 ps -ef| grep atm |grep -v grep |awk '{print $2}'|xargs kill -15 容器中的进程捕获 SIGTERM 信号,优雅的退出,发现所有容器中的进程都退出后再执行发布逻辑 缺点: 这个方案肯定是没有问题的,但是如果进程同名,很有可能会导致误杀,而且从docker容器外部,传信号给容器内部的进程,感觉有点奇怪 2)方案2 幸运的看到了参考资料1 root@xxxx:~/test/docker/test_dk$ docker stop --help Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...] Stop a running container by sending SIGTERM and then SIGKILL after a grace period --help=false Print usage -t, --time=10 Seconds to wait for stop before killing it 在docker stop命令执行的时候,会先向容器中PID为1的进程发送系统信号SIGTERM,然后等待容器中的应用程序终止执行,如果等待时间达到设定的超时时间,或者默认的10秒,会继续发送SIGKILL的系统信号强行kill掉进程。在容器中的应用程序,可以选择忽略和不处理SIGTERM信号,不过一旦达到超时时间,程序就会被系统强行kill掉,因为SIGKILL信号是直接发往系统内核的,应用程序没有机会去处理它。在使用docker stop命令的时候,我们唯一能控制的是超时时间,比如设置为20秒超时: ...

January 1, 2018 · 1 min