美文网首页
Golang 游戏leaf系列(七) 监听关闭

Golang 游戏leaf系列(七) 监听关闭

作者: 合肥黑 | 来源:发表于2019-05-31 10:56 被阅读0次

    Golang 学习笔记十一 os/signal包 和 实例runner看到对系统关闭的侦听方式:

    func main() {
        c := make(chan os.Signal, 0)
        signal.Notify(c)
     
        // Block until a signal is received.
        s := <-c
        fmt.Println("Got signal:", s) //Got signal: terminated
    }
    

    在leaf.go中也有同样的处理:

    ...
        // close
        c := make(chan os.Signal, 1)
        signal.Notify(c, os.Interrupt, os.Kill)
        sig := <-c
        log.Release("Leaf closing down (signal: %v)", sig)
        console.Destroy()
        cluster.Destroy()
        module.Destroy()
    ...
    

    重点关注一下module.Destroy

    一、mudule.Destroy
    func Destroy() {
        for i := len(mods) - 1; i >= 0; i-- {
            m := mods[i]
            m.closeSig <- true
            m.wg.Wait()
            destroy(m)
        }
    }
    
    type module struct {
        mi       Module
        closeSig chan bool
        wg       sync.WaitGroup
    }
    
    var mods []*module
    

    在之前的系列中,也介绍过module的运行方式。可以看到Destroy中,对切片里的所有module进行了反序destroy,但是destroy相关代码有点复杂,看看别的方法:

    func Register(mi Module) {
        m := new(module)
        m.mi = mi
        m.closeSig = make(chan bool, 1)
    
        mods = append(mods, m)
    }
    
    func Init() {
        for i := 0; i < len(mods); i++ {
            mods[i].mi.OnInit()
        }
    
        for i := 0; i < len(mods); i++ {
            m := mods[i]
            m.wg.Add(1)
            go run(m)
        }
    }
    
    func run(m *module) {
        m.mi.Run(m.closeSig)
        m.wg.Done()
    }
    

    这里的Register和Init方法,是由leaf.go里先后调用的。看起来,每个module都交给了一个协程go run(m)去处理,但是在run之前,先设定了一个waitgroup加1,等run真的执行完了,waitgroup再执行Done。那么在这个执行过程中,如果收到了Destroy怎么办呢,答案是等执行完再接着Destroy。到底在等什么,其实就是在等Run而已

            m.closeSig <- true
            m.wg.Wait()
            destroy(m)
    

    先写进去一个标记告诉后面要执行run的,我们要销毁了。然后waitgroup就Wait阻塞了,等执行完调用wg.Done时,再接着执行destroy(m)。

    注意,每个Module的接口里,Run方法都带了一个标记

    type Module interface {
        OnInit()
        OnDestroy()
        Run(closeSig chan bool)
    }
    

    这个方法传的是channel引用,传的正是m.mi.Run(m.closeSig)。上面说在等Run处理完再destroy,其实Run里面会在m.closeSig这里也做销毁的事情

    比如gate.go里的Run:

    ...
        if wsServer != nil {
            wsServer.Start()
        }
        if tcpServer != nil {
            tcpServer.Start()
        }
        <-closeSig
        if wsServer != nil {
            wsServer.Close()
        }
        if tcpServer != nil {
            tcpServer.Close()
        }
    }
    

    在启动服务器后,就一直在这里等结束。只有wsServer,tcpServer都Close了,这个Run才算完成,才能接着上面说的,继续执行destroy(m)

    再看看skeleton.go的Run

    func (s *Skeleton) Run(closeSig chan bool) {
        for {
            select {
            case <-closeSig:
                s.commandServer.Close()
                s.server.Close()
                for !s.g.Idle() || !s.client.Idle() {
                    s.g.Close()
                    s.client.Close()
                }
                return
            case ri := <-s.client.ChanAsynRet:
                s.client.Cb(ri)
            case ci := <-s.server.ChanCall:
                s.server.Exec(ci)
            case ci := <-s.commandServer.ChanCall:
                s.commandServer.Exec(ci)
            case cb := <-s.g.ChanCb:
                s.g.Cb(cb)
            case t := <-s.dispatcher.ChanTimer:
                t.Cb()
            }
        }
    }
    

    都关完了,直接return掉,可以接着destroy了。

    真正要去destroy就简单了,先调用OnDestroy,然后错误处理:

    func destroy(m *module) {
        defer func() {
            if r := recover(); r != nil {
                if conf.LenStackBuf > 0 {
                    buf := make([]byte, conf.LenStackBuf)
                    l := runtime.Stack(buf, false)
                    log.Error("%v: %s", r, buf[:l])
                } else {
                    log.Error("%v", r)
                }
            }
        }()
    
        m.mi.OnDestroy()
    }
    

    相关文章

      网友评论

          本文标题:Golang 游戏leaf系列(七) 监听关闭

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