New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
服务端崩溃 #111
Comments
你改了代码吗?似乎和源码对不上。另外,你这个是崩溃的 log? |
我只加了一个方法,在agent的接口里面,写帧消息用的。其它没有动,同时开个300个连接就会奔溃,几十个连接同时运行逻辑没有问题。 |
我配置了log路径,但是好像这些奔溃的log记录不了。 goroutine 1000 [IO wait]: |
leaf 不是这样用的。你还需要再看看文档。 |
除非非常清楚,否则不要改动任何 leaf 的源码。可以改动 leafserver 的代码。 |
我就是按照leaf-server的demo上面加的,leaf源码加了一个方法。 |
问题就出在你加的这个方法上。 |
谢了,我把leaf源码还原看看,如果再连接多了再崩溃我在回来请教你。这个周要上线,几百个连接就崩溃的那瞬间我快哭了@@,第一次用golang表示鸭梨大。 |
我在很长的一段时间,测试过很多次数的上万次的连接,对实际游戏中的多种不同的功能做压力测试,并没有出现过问题。之前有其他同学用更好的机器,更多的连接数测试过,没有出现过问题。实际中使用 leaf 的游戏在这几年的运行中也没有发现过网络层的问题。所以,尽量不要动 leaf 的源码,这会让解决问题变的很复杂。 |
我删除了,重新go get, 还是出现这个问题。 |
是不是和锁有关系 |
锁应该不会导致崩溃。而且 leaf 里面是不用锁的。你是什么场景下需要用锁? |
我是一局游戏开了一个Goroutine, leaf game模块负责消息的转发,创建游戏局。消息来了再game模块有改变玩家的状态,而游戏局的Goroutine要清算玩家的数据,所以就在游戏房间的struct上加了sync.Mutex。这样会不会出问题。 |
好像不会有问题。 |
我试了,锁我注释了。还是会崩溃,我试试是不是消息的问题。 |
我只发心跳空消息,leaf收到后就回复,客户端1-5秒发送一次。2w 个客户端没有问题。 root@ubuntu:~# netstat -antp | grep leaf | wc -l |
逻辑上使用多 goroutine 容易出问题。感觉就是在用多线程写逻辑一样,不是一个好的选择。 |
我只有一个保存数据有了chan + goroutine, 还有每个游戏局开一个goroutine. |
没有建议。找问题,只能拿着代码,一点一点分析。没有代码,只能是雾里看花了。对于设计,总的来说,很重要的是“简洁”。好好把握这个,实际中就不会出大问题。 |
看来今晚要加班搞了。苍天啊,大地啊。 |
该加班的还得加班:) leaf 在很早期,雏形阶段,用来做过棋牌游戏,也是在逻辑上使用了多 goroutine,总的来说,不是特别好的实践。不过实际情况也还行,维持同时上千人在线毫无压力。多 goroutine 的代码,每一行都可能带来额外的心智负担。 |
找到原因了,我自己写了一个计算sync.Map{}的len方法引起的,改成了你的util.Map,现在已经跑到了几万个。还有一个问题就是windows和虚拟机的问题,我放到了VPS。打开了文件数限制。现在不报错了,只是1核心跑1万局游戏,有点慢。:) |
不错,不用加班了:)细节慢慢优化。 |
嗯,优化的地方还有很多,要好好学习一下才行。你很喜欢海贼王么,头像都是路飞 : 0 |
免责声明:如果只有发送方的go-routine,没有其他的go-routine,那么会发生死锁,go程序会检测出死锁并崩溃。 |
@jjjjilyf 什么意思 |
之前你写的那个崩溃的地方 是不是这个引起的 |
是RACE竞争引起的,多个goroutine访问了同一个变量。现在没有问题,但是我加锁和用util.Map解决的。感觉后期还得重构一遍 |
啊 |
请问下我也用了util.map但是获取不了 里面的数据,里面存了key从1-20的数据,用map.Get(1)获取不到数据。 |
那肯定你是用的有问题,和util.Map没有关系 |
我找了下原因,是因为 我的key存的时候是uint16,取得时候是uint32.。。。 |
请问下 你们是怎么测试的?比如游戏里的匹配,存取数据库,这些是不是建立1000000个Client并建立和服服务器的连接,把那些卡住程序执行的条件去掉?用go 来发送消息?这样测试服务器的负载情况吗? |
For循环里面有个时间间隔,时间越短,服务器的压力会越大。差不多就行了。我们是一个答题的游戏,2G内存开个2-3万跑得动。 |
多谢啦大哥^^ 我参考写一下,我们是一个足球观战微信游戏。。。 |
我们用的FlatBuffer协议,你们用的是Json吗? |
肯定用google protobuf 啊 |
本来想用的pb的后来改成fb了,不用序列化和反序列化,听说专门为游戏做的协议 |
请问下是不是在执行 go test的时候 也会默认执行包里包含的init()函数,因为找不到server.json各种报错 |
请问下,你是如何设计“一局游戏开一个Goroutine”的。能否给个简单的demo或者思路。 |
没错,就是这样设计 |
@zcwtop 为什么每个操作都要单独开goroutine。我之前就是开太多了,几十局游戏开运行完全跑不出问题。跑几万的次数就崩溃,这就是我开个issue求助的原因,找BUG找半天。我听了作者了。尽量少开,一局游戏的逻辑操作都放一个goroutine里执行,数据同步放到另外一个goroutine专门处理,我们用的Postgress。这样防止并发下未知的RACE。简单点好 |
@exfun 是啊。我现在感觉是太多了。我现在的设计师是这样的,假如一局游戏的结构是 func (t *T) del { 基本上每个操作都是这样。 如果改成一个go运行,我改如何改? 或者你是如何写的,能否给个参考。 |
我没有用leaf这个,用go 关键字声明的。leaf的只用了它的消息和工具类这些。 |
@exfun 那是不是这个Process是一个死循环,用select多路复用等待通道消息,然后执行
} |
写好点应该是这样的,leaf负责消息转发chan通知,来了就处理,注意游戏结束条件。别一直结束不了卡住,这样就会有问题。超时的地方得处理!我们是在线答题的游戏,比较简单。 |
thank you。感谢解答。我得重新设计一下。 |
照这样写的话 还需要框架吗?都是自己处理了 |
@jjjjilyf 我想两种方式都写一次,然后都压力测试一下。 |
@exfun 请问Util.map里Get到的元素怎么判断是否为nil,用 == nil 来判断,判断不出来,存的是个指针类型的数据。 |
@exfun 哈哈,遇到了同样的问题 |
@zcwtop 人多的时候一定要注意别资源竞争(多核下就很容易崩溃),要么用锁,原子,条件变量控制。或者用chan来传递消息。以下是我一个Game一个goroutine来控制游戏过程的流程Process() 。重构后的代码,比之前清晰多了,这也是我第一次写golang,还需要学习的东西很多。
|
@exfun 我的错误也是写map没加锁导致的。这个错误捕获还挺不容易,报错了要马上截图才看到。后面tcp等看到的错误都是这个引起的。leaf框架本身没有问题,都是自己的代码逻辑错误引起的。 我现在一局游戏的逻辑跟你上面的代码是类似的。 自己写的机器人暂时只开最多5000个测试,先把断线重连逻辑完善了再压。 |
这个issue可以留给那些后来的人,一定要注意map哈哈。希望遇到同样的问题的人能尽快解决问题,如果崩溃了。和框架没有关系,一定是自己埋地雷了,找同步,竞争问题。 |
同时开几百个客户端出现奔溃,大神帮忙看看。
goroutine 935 [IO wait]:
internal/poll.runtime_pollWait(0x7fa51f17b400, 0x72, 0x0)
C:/Go/src/runtime/netpoll.go:173 +0x57
internal/poll.(*pollDesc).wait(0xc420432a98, 0x72, 0xffffffffffffff00, 0xf42ba0, 0xf3c6b8)
C:/Go/src/internal/poll/fd_poll_runtime.go:85 +0xae
internal/poll.(*pollDesc).waitRead(0xc420432a98, 0xc42052a700, 0x2, 0x4)
C:/Go/src/internal/poll/fd_poll_runtime.go:90 +0x3d
internal/poll.(*FD).Read(0xc420432a80, 0xc42052a700, 0x2, 0x4, 0x0, 0x0, 0x0)
C:/Go/src/internal/poll/fd_unix.go:126 +0x18a
net.(*netFD).Read(0xc420432a80, 0xc42052a700, 0x2, 0x4, 0xc42145bdd8, 0xc42145bd50, 0x3)
C:/Go/src/net/fd_unix.go:202 +0x52
net.(*conn).Read(0xc420532560, 0xc42052a700, 0x2, 0x4, 0x0, 0x0, 0x0)
C:/Go/src/net/net.go:176 +0x6d
github.com/name5566/leaf/network.(*TCPConn).Read(0xc420fb7d40, 0xc42052a700, 0x2, 0x4, 0xc42052a700, 0xc42145bdf8, 0x0)
D:/goprojects/src/github.com/name5566/leaf/network/tcp_conn.go:96 +0x52
io.ReadAtLeast(0xf40720, 0xc420fb7d40, 0xc42052a700, 0x2, 0x4, 0x2, 0xa966c0, 0xc4201b4201, 0xc42052a700)
C:/Go/src/io/io.go:309 +0x86
io.ReadFull(0xf40720, 0xc420fb7d40, 0xc42052a700, 0x2, 0x4, 0xc420172f60, 0xc421636fc2, 0x2b)
C:/Go/src/io/io.go:327 +0x58
github.com/name5566/leaf/network.(*MsgParser).Read(0xc42040fec0, 0xc420fb7d40, 0xc421636fc0, 0x0, 0x0, 0x0, 0x0)
D:/goprojects/src/github.com/name5566/leaf/network/tcp_msg.go:70 +0x85
github.com/name5566/leaf/network.(*TCPConn).ReadMsg(0xc420fb7d40, 0xb38da0, 0xc42163c510, 0xb386a0, 0xc420fb7d70, 0x0)
D:/goprojects/src/github.com/name5566/leaf/network/tcp_conn.go:108 +0x34
github.com/name5566/leaf/gate.(*agent).Run(0xc420fb7d70)
D:/goprojects/src/github.com/name5566/leaf/gate/gate.go:94 +0x3a
github.com/name5566/leaf/network.(*TCPServer).run.func1(0xf44160, 0xc420fb7d70, 0xc420fb7d40, 0xc42051a000, 0xf4b7a0, 0xc420532560)
D:/goprojects/src/github.com/name5566/leaf/network/tcp_server.go:102 +0x35
created by github.com/name5566/leaf/network.(*TCPServer).run
D:/goprojects/src/github.com/name5566/leaf/network/tcp_server.go:101 +0x40a
The text was updated successfully, but these errors were encountered: