引言
使用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服务
addr := fmt.Sprintf("%v:%v", context.GlobalConfig.Bind.Host,
context.GlobalConfig.Bind.Port)
ln, err := net.Listen("tcp4", addr)
if err != nil {
log.WithField("error", err).Error("start server fail")
os.Exit(-5)
}
// 最大等待3秒
Listener = NewGracefulListener(ln, time.Second * 3)
err = fasthttp.Serve(Listener, router.Handler)
if err != nil {
log.WithField("error", err).Error("start server fail")
os.Exit(-5)
}
log.Info("server stop listen")
完整项目地址:
https://github.com/vearne/hello_server