skynet 下的用户登陆问题
今天收到一个朋友的邮件,他们使用 skynet 框架的游戏上线后遇到一些问题。
引用如下:
我有一个线上的 server 用了skynet框架,用了gate watchdog agent这样的结构,当人数到达1000左右的时候发现很多新 socket 连接都阻塞在了 gate 或 watchdog 这里,导致用户登录不了,后面发现是客户端那边在登录的时候做了超时判断,如果超时了就断开连接,重新连 server 执行登录过程,因为 watchdog 是在收到客户端发来的第一个消息的时候创建agent,这就导致很多 cpu 消耗在创建销毁 agent 上,而 agent 服务创建的代价比较高,这就导致watchdog效率下降。后来取消掉了客户端的那个登录超时判断,情况有所好转,但在人多的时候延迟还是存在,接着又优化了server 这边的一些逻辑代码,现在还在观察中。
中午吃饭的时候,我和我们的开发人员讨论了这个问题。为什么我们在 1000 人级别时没有出现类似状况呢?我总结的原因如下:
我们把用户认证过程放在了 agent 启动之前,是由 watchdog 进行的。
这样,未认证连接在认证前是不会启动新的 agent 的。这避免了远超过系统承载能力的并发连接引起的问题。
这样做,会让 watchdog 的逻辑复杂一些,它需要先统一处理 gate 转发过来的数据包,直到认证结束,然后通知 gate 做包转发。watchdog 承载的任务也多一些,但这么做看起来是值得的。
agent 启动是一个 cpu 开销很大的操作。skynet 的 serivce 对象,本身是很轻量的。lua vm 的创建也不会有什么开销。但是在一个新的 lua vm 上初始化 lua 代码却会消耗大量的时间。
如果 agent 的启动全部由 watchdog 完成,就必然被串行化,并会阻塞住 watchdog 消化其它包。当多人同时登陆时势必是一个性能瓶颈。尤其是每个新建连接就立刻创建新的 agent 的策略,当用户登陆不上反复重试,更会将问题恶化。
最后,我修改了一下 skynet 中 lua 服务的启动流程,可能会对这种情况有所改善。
我将 lua 服务的启动流程改为两步,先创建出空的 lua vm 。然后注册一个专用于启动的消息处理函数,并立刻给自己发一个启动消息。这个消息一定是消息队列里的第一个消息。
接下来由这个启动消息来触发 lua vm 的进一步初始化过程。这样,就可以充分利用多核来处理并发登陆请求了。
这个修改可能引起的问题是,启动服务的调用无法侦测到启动失败。
最后,我认为,针对并发登陆的排队处理总是要做的。由 watchdog 转发登陆认证请求到一个排队服务器中去应该是一个更好的解决方案。