内容简介:在查看一、Q:leaf的log蛮好用的了,可以log到文本。但是现在需要做日志分析。要能支持json格式,异步log等等。希望可以加强下。
在查看 官方issues 时,也发现了一些个人觉得有用的讨论,摘录一下
Q:leaf的log蛮好用的了,可以log到文本。但是现在需要做日志分析。要能支持json格式,异步log等等。希望可以加强下。
看了下zap日志库,感觉过去复杂了。有没有简单实用,能分析的日志库?
A:Leaf 的 log 的定位并非用于数据分析,最好额外加一套用于数据分析的机制,同时也保留 Leaf 的 log。
二、 issues 服务端崩溃
zcwtop:请问下,你是如何设计“一局游戏开一个Goroutine”的。能否给个简单的demo或者思路。
我自己写,一局游戏基本上是每个操作都放在goroutine里运行的,用的是leaf的顺序goroutine。这样感觉goroutine会非常多,不知道这样设计是不是错了?
exfun:为什么每个操作都要单独开goroutine。我之前就是开太多了,几十局游戏开运行完全跑不出问题。跑几万的次数就崩溃,这就是我开个issue求助的原因,找BUG找半天。我听了作者了。尽量少开,一局游戏的逻辑操作都放一个goroutine里执行,数据同步放到另外一个goroutine专门处理,我们用的Postgress。这样防止并发下未知的RACE。简单点好
zcwtop:是啊。我现在感觉是太多了。我现在的设计是这样的,假如一局游戏的结构是
type T struct { *g // leaf 平行的go ... } func (t *T) add { t.Go(func() { // ... }, func(){ //... }) } func (t *T) del { t.Go(func() { // ... }, func(){ //... }) }
基本上每个操作都是这样。如果改成一个 go 运行,我该如何改? 或者你是如何写的,能否给个参考。
exfun:我没有用leaf这个,用go 关键字声明的。leaf的只用了它的消息和 工具 类这些。我是一局游戏比如game *Game, 然后会在一个goroutinue里面执行,比如 go game.Process() , 然后在process里面完成单局所有的事情。游戏局完了之后,这个goroutine就会执行完,等待GC回收。
zcwtop:那是不是这个Process是一个死循环,用select多路复用等待通道消息,然后执行。我理解是不是类似这样处理:
func (g *Game) Process(...) { begin() //... for { select { case c:=<-通道1: handle1() case c := <-通道2: handle2() } } //... end() } func (g *Game)handle1() { //... } func (g *Game)handle2() { //... }
exfun:写好点应该是这样的,leaf负责消息转发chan通知,来了就处理,注意游戏结束条件。别一直结束不了卡住,这样就会有问题。超时的地方得处理!我们是在线答题的游戏,比较简单。
zcwtop:人多的时候一定要注意别资源竞争(多核下就很容易崩溃),要么用锁,原子,条件变量控制。或者用chan来传递消息。以下是我一个Game一个goroutine来控制游戏过程的流程Process() 。重构后的代码,比之前清晰多了,这也是我第一次写golang,还需要学习的东西很多。
func (GR *GameRoom) Process() { // 处理游戏过程结束/正常/异常 defer func() { GR.End() if e := recover(); e != nil { panic(e) } }() GR.Playing = true gameStartTime := conf.GameConfig.StartTime robotTime := conf.GameConfig.RobotTime Playing: for { select { case m := <-GR.MsgChan: player := m.P switch m.Mtype { case MSG_JOIN: GR.onJoin(player) case MSG_ANSWER: GR.onAnswer(player, m.Answer, m.Pm) } case wf := <-GR.WaitChan: switch wf { case WAIT_GAME: // 游戏开始倒计时,补充一部分AI joinRobots(GR, utils.RandIntFrom2(18, 25)) // 等待是否补充足房间 time.AfterFunc(time.Second*time.Duration(robotTime), func() { GR.WaitChan <- ON_RO_JOIN }) case ON_RO_JOIN: // 不足AI joinRobots(GR, 0) // 剩余倒计时 time.AfterFunc(time.Second*time.Duration(gameStartTime-robotTime), func() { GR.WaitChan <- ON_GAME_START }) case ON_GAME_START: // 游戏开始 GR.Start() case ON_RO_ANS_1: // 机器人第一次回答 helpRobotAnswer(GR, true, false) time.AfterFunc(time.Second*time.Duration(1), func() { GR.WaitChan <- ON_RO_ANS_2 }) case ON_RO_ANS_2: // 机器人第二次回答 helpRobotAnswer(GR, false, false) time.AfterFunc(time.Second*time.Duration(1), func() { GR.WaitChan <- ON_RO_ANS_3 }) case ON_RO_ANS_3: helpRobotAnswer(GR, false, true) // 倒计时回答结束 time.AfterFunc(time.Second*time.Duration(GR.AnswerTimeout-4), func() { GR.WaitChan <- ON_ANSWER_END }) case ON_ANSWER_END: // 回答结束,结算 GR.onAnswerOver() case ON_SEND_QUESTION: // 出题 GR.sendQuestion() case MSG_END_GAME: // 游戏结束 //break game break Playing // goto END } } } //END: log.Debug("Game goroutine over ->%s", GR.Uuid) }
游戏服务器一定需要用户管理,最常见的方式就是建立用户 ID 到用户实例的映射关系(还有可能存在用户帐号 ID、用户名到用户实例的映射关系)。例如:
users = make(map[int]*User)
User 本身为了简单,可以直接组合 Agent:
type User struct { gate.Agent }
这样的话,广播消息就是:
for _, user := range users { user.WriteMsg(msg) }
一个最简单的广播的例子:打开 Leafserver game/internel/chanrpc.go 文件,加入一个全局变量。
var agents = make(map[gate.Agent]struct{}) agents 的管理: // agent 被创建时 func rpcNewAgent(args []interface{}) { a := args[0].(gate.Agent) agents[a] = struct{}{} } // agent 被关闭时 func rpcCloseAgent(args []interface{}) { a := args[0].(gate.Agent) delete(agents, a) } 由此可见 agents 中保存了当前所有连接,广播的处理: for a := range agents { a.WriteMsg(msg) }
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- progit摘录笔记
- Objective-C 消息转发应用场景摘录
- 亦来云被声讨“八宗罪” 官方群和官方已决裂
- Substrate 官方教程增强版
- Babel 插件开发手册(官方)
- [译]Kafka官方文档-快速入门
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
未来世界的幸存者
阮一峰 / 人民邮电出版社 / 2018-6-1 / 39.00 元
本书为阮一峰博客文集,主要收录的是作者对技术变革的影响的一些思考,希望能够藉此书让读者意识到世界正在剧烈变化,洪水就在不远处,从而早早准备出路。本书适合所有乐于思考的读者。一起来看看 《未来世界的幸存者》 这本书的介绍吧!