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

在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系列(七) 监听关闭)