美文网首页GoGo语言Go知识库
Go FastHttp优雅关闭实现方案

Go FastHttp优雅关闭实现方案

作者: 司青玄 | 来源:发表于2018-07-31 14:47 被阅读4次

    使用Go开发web服务时很多情况下都会使用号称比标准库快10x的FastHttp, 但fasthttp(版本: 20180529.0.0)至今也没有提供优雅关闭的方法,默认情况下退出服务只能kill。下面谈几个实现方案。

    首先,要明确所谓的优雅关闭是要求我们在调用close()时要做到以下几点:

    • 拒绝接受新连接
    • 等待正在处理的请求完成,然后关闭连接
    • 关闭剩余空闲的连接

    要做到第一点,我们需要重写一下net.Listener实现,例如叫GraceListener, 在此结构体中组合一个真正干活的Listener,覆盖Close()方法,在此方法中先将干活的Listener关闭,此时就不会再监听新请求了,然后再block当前routine直到所有连接全部关闭为止。代码如下:

    type graceListener struct {
        net.Listener
    }
    
    func (gl *graceListener) Close() error {
        err := gl.Close()
        if nil != err {
            return err
        }
    
        // block, 直到所有连接关闭
    }
    
    fastServ := &fasthttp.Server{
            Concurrency:  100,
            Handler:      xxxFunc,
            LogAllErrors: true,
        }
    
        ln, err := net.Listen("tcp4", ":8080")
        if nil != err {
            // err
        }
    
        graceLn := &graceListener{
            Listener: ln,
        }
    
        fastServ.Serve(graceLn)
    

    接下来看看如何满足后面两条要求。这里有两种方案,第一种最简单的方案是,保存一个全局的sync.WaitGroup指针,在你的请求处理函数中,先调用wg.Add(1), 然后defer wg.Done(), 最后在上面的Close()方法中使用wg.Wait()即可。这里建议最好使用select给等待加个超时功能,即如果超过指定时间还没有退出则强制退出:

    // 此方法一直block到所有请求退出或超时
    func WaitForGracefullyClose() error {
        select {
        case <-waitAllRoutineDone():
            return nil
    
        case <-time.After(maxWait):
            return fmt.Errorf("force shutdown after %v", maxWait)
        }
    
    }
    
    // 等待所有请求处理routine完成;
    // 此方法返回只有1个缓冲的channel, 只有当所有routine结束时channel才会有元素
    func waitAllRoutineDone() chan struct{} {
        flagChan := make(chan struct{}, 1)
    
        go func() {
            wg.Wait()
    
            flagChan <- struct{}{}
        }()
    
        return flagChan
    }
    

    此外还有第二种方案,那就是在自己的graceListener中添加一个计数器用于统计当前的连接数,重写Accept()方法,将计数器+1,再定义一个自己的套壳net.Conn实现,重写Close()方法,在里面将计数器-1。这里要注意线程安全问题,最好使用atomic包进行操作。最后在graceListener#Close()中关闭Listener后等待计数器归零。这种方案有以下几个缺点:

    • 实现繁琐
    • 无法处理keep-alive连接。即请求已经处理完成,但是连接并没有关闭,这时计数器不会归零。不过可以使用go 1.3新增加的 Conn State Hook来实现当连接状态变更时的通知,但是也是比较繁琐的。

    因此不推荐这种方案。

    完成Http Server的关闭后,就可以添加一些清理自己业务资源的逻辑了,比如关闭数据库连接,redis连接,取消注册,刷新日志等。

    相关文章

      网友评论

        本文标题:Go FastHttp优雅关闭实现方案

        本文链接:https://www.haomeiwen.com/subject/awgcvftx.html