如何优雅的停掉Server

引言

俗话说的好,请神容易,送神难。同样,一个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的一点思考,如有不对,欢迎指教。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容