美文网首页Go语言
如何优雅的停掉Server

如何优雅的停掉Server

作者: carlSQ | 来源:发表于2018-02-08 18:52 被阅读115次

引言

俗话说的好,请神容易,送神难。同样,一个Server启动起来也很容易,但怎么退出,直接kill,还是直接退出main函数?

优雅的停掉Server

直接kill,或是直接退出main函数,这种方式很粗暴,可能会导致业务数据的损坏,不完整,丢失。
那应该怎么停掉Server。这里笔者以Thrift Serve为列子。看看Thrift 源码里面的一段停掉Server代码:


func (p *TSimpleServer) AcceptLoop() error {
    for {
        client, err := p.serverTransport.Accept()
        if err != nil {
            select {
            case <-p.quit:
                return nil
            default:
            }
            return err
        }
        if client != nil {
            p.Add(1)
            go func() {
                if err := p.processRequests(client); err != nil {
                    log.Println("error processing request:", err)
                }
            }()
        }
    }
}

var once sync.Once

func (p *TSimpleServer) Stop() error {
    q := func() {
        close(p.quit)
        p.serverTransport.Interrupt()
        p.Wait()
    }
    once.Do(q)
    return nil
}


.......
func (p *TServerSocket) Interrupt() error {
    p.mu.Lock()
    p.interrupted = true
    p.Close()
    p.mu.Unlock()

    return nil
}

func (p *TServerSocket) Close() error {
    defer func() {
        p.listener = nil
    }()
    if p.IsListening() {
        return p.listener.Close()
    }
    return nil
}


当调用Stop 停掉服务的时候关闭 p.quit chan,p.serverTransport.Interrupt()会关闭端口监听,这时候 client, err := p.serverTransport.Accept() 会抛出错误。这时 AcceptLoop 会结束,进程阻塞在p.Wait(),等待 processRequests goroutine 结束。
这种方式比直接kill,或是直接退出main函数优雅多了。但还是存在一些问题。

  1. 如果Server goroutine 死锁了,这时候服务都不能顺利退出。

  2. 只是针对了Server goroutine 等待, 但 Server goroutine 可能会开启一些 client goroutine ,而且可能还有一些manager goroutine。

  3. Server在Wait 过程中,Client 还会尝试调用Server,这时候Client 会一直报错。

针对一中的问题,加入等待超时机制,防止这种问题。超时时间确保所有收到的请求能处理完。

// AcceptLoop loops and accepts connections.
func (p *TSimpleServer) AcceptLoop() error {
    for {
        client, err := p.serverTransport.Accept()
        if err != nil {
            select {
            case <-p.quit:
                return nil
            default:
            }
            return err
        }
        if client != nil {
            p.Add(1)
            go func() {
                if err := p.processRequests(client); err != nil {
                    log.Println("error processing request:", err)
                }
            }()
        }
    }
}

var once sync.Once

func (p *TSimpleServer) Stop() {
     q := func() {
          close(p.quit)
          p.serverTransport.Interrupt()
          timer := time.NewTimer(p.GracefulTimeout)
          waitCh := make(chan struct{})
           go func() {
             p.Wait()
             close(waitCh)
            }()
            select {
                     case <-waitCh:
                     case <-timer.C:
             }
          }
          once.Do(q)
          return nil
}

针对二中的问题,定义一个 Observer interface,相应的goroutine 里注册监听,实现stop 处理。当Server stop的时候发送Shutdown消息。

type ShutdownObserver interface {
    ShutdownNotify(evt interface{})
}

......
func (n *ShutdownNotifier) Notify(evt interface{}) {
    n.RLock()
    for o := range n.observers {
        o.ShutdownNotify(evt)
    }
    n.RUnlock()
}

func (p *TSimpleServer) Stop() {
     q := func() {
          close(p.quit)
          p.serverTransport.Interrupt()
          timer := time.NewTimer(p.GracefulTimeout)
          waitCh := make(chan struct{})
           go func() {
             p.shutdown.Notify(nil)
             p.Wait()
             close(waitCh)
            }()
            select {
                     case <-waitCh:
                     case <-timer.C:
             }
          }
          once.Do(q)
          return nil
}

针对三中的问题, 以soa 服务为例,当stop 的时候,把当前机器从服务注册中心当前所在集群中踢掉,client 会更新集群机器列表,不去访问当前机器。

总结

以上是对停掉Server的一点思考,如有不对,欢迎指教。

相关文章

  • 如何优雅的停掉Server

    引言 俗话说的好,请神容易,送神难。同样,一个Server启动起来也很容易,但怎么退出,直接kill,还是直接退出...

  • golfing 如何停掉channel

    通过close channel 通过发送消息机制

  • 如何优雅

    如何优雅?这是一个很流行,也很值得追问的问题。 如何优雅地读书?如何优雅地喝茶?如何优雅地跑步?如何优雅地追求女神...

  • 年薪20万Python工程师进阶(3):Python开发之理解W

    本文目录 什么是WSGI? 如何实现Application? 如何实现Web Server? Web Server...

  • 《一款清理win10隐私的免费小众软件》

    推荐导读:《如何优雅的使用Chrome?》 《如何优雅的管理自己的密码 ?》 《如何优雅的 免费 使用 正版 Ad...

  • 2018-11-29

    mysql 的密码如何更改 1、停掉服务 2、mysqld_safe --skip-grant-tables -...

  • 如何优雅地使用 KVO

    如何优雅地使用 KVO 如何优雅地使用 KVO

  • 如何 如何

    如何 如何 如何走去你面前 用理解,用怀念。 如何停掉你的风雪 用太阳,用明天。 我同陨石坠落 为的是追赶 你 有...

  • 探寻一个优雅的工作方式

    如何优雅到不急不慢,自由神闲 如何优雅到有理有据,泰然自若 如何优雅到内心宁静,波澜不惊 如何优雅到刀枪不入,固若金汤

  • 停掉小说

    坚持每天打卡已经21天,基本上养成习惯了,后面持续就可以了!现在我需要一个新的目标,想了想(虽然只用了很短的时间去...

网友评论

    本文标题:如何优雅的停掉Server

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