Golang 游戏leaf系列(七) 监听关闭
2019-05-31 本文已影响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()
}