« Lua 字节码与字符串的共享 | 返回首页 | 相位技术的实现 »

开发笔记(29) : agent 跨机 id 同步问题

我们的大部分设计是围绕单个进程进行的,所有的数据在进程内都可以方便的共享。只需要这些数据结构是线程安全的即可。

但最终,我们不会在单台机器上运营整个游戏服务器,所以还是要考虑玩家在不同物理机器间移动的问题。

虽然我们还没有开始进行跨机方面的开发,但是不少服务已经要开始考虑这个问题了。目前、每个玩家接入游戏服务器并认证完毕后,都会有一个 lua 虚拟机伴随他。也就是我称之为 agent 的东西。

agent 和场景服务 map 间会有高频率的互动,所以,我们必须要求 agent 和玩家所在 map 在同一个进程内。当玩家跳转到不在同一进程内的 map 上时,需要把 agent 迁移到对应的进程内。

迁移 agent 并不算难做:只需要把 agent 的数据持久化,然后在新的进程内启动起来即可。难点在于,新的 agent 的通讯 handle 变化了。为了性能考虑,我并不想实现一套跨机器的唯一 id 系统。那么,就需要解决一个问题:如果 handle 发生变化,如何通知持有这个 handle 的服务模块。

map 这样的服务是不需要持有远程 handle 的,所有它处理的 agent 一定和它同属一个进程内。但其它一些对通讯延迟要求不高的服务就不是这样了。比如队伍管理、聊天室等等。简单的做法是启用一套独立的 id 系统,但这套 id 系统不用放在底层去破坏底层的简洁性。但如果利用 skynet 固有提供的字符串名字机制,又觉得效率低下。( skynet 在设计服务别名系统的时候,没有打算为上千个名字去服务)

一个线程安全的 hash 表就可以达到目的。我开启了一个有 64K 个 slot 的 hash 表,以玩家 id 做索引,映射 agent 的 handle 。这是一个 C 模块,所以可以共享由不同的 lua 模块查询和更新。对于跨机服务,还是和跨机组播那样的老方法,在每台机器上启动一个代理服务,而系统中有一个唯一的中心管理服务。每当对应表更新后,就通知所有的机器上的 hash 表同步更新。

这个 hash 表在实现线程安全时,我曾经考虑做无锁的数据结构,但很难回避释放空闲节点的问题。没有 GC 的语言实现无锁 hash 表是很困难的。但从另一个角度优化锁却很简单:只需要给每个 slot 单独上锁,实际上冲突的可能性极低。


在考虑这个部分的实现时,我曾经考虑过另一个方案:不引入新的 id 系统,而是在 hash 表中记住所有 handle 的关联关系。比如 handle 1 迁移走变成 handle 2 后,就在 hash 表中记住 1 和 2 都映射为 2 。无论 agent 迁移几次,都可以从任何一个历史版本查询到现在的最新 handle 。做一个简单的估算,这个方案在空间消耗上也是可行的。查询速度应该在一个数量级。

不过我们的游戏在事实上已经有了一套玩家 id 系统,所以用额外的 id 系统做映射也是很自然的事情了。(后一种方案的好处是不必引入新的 id )

Comments

把 agent 的数据持久化 是用_luaseri_pack _luaseri_unpack来实现吗?
没做过游戏开发,不过可以看看前辈的经验,对于多并发的各种思路,谢谢了。
您的博客对我非常有用,感谢一直以来的分享,希望能能多多学习。www.tyebh120.com
"agent 和场景服务 map 间会有高频率的互动" 这个需求也许可以打破,按照我的理解 map 服务器最主要的作用其实是记录并广播角色的位置信息,而 agent 与 map 之间是受信的,所以可能“高频互动”是不需要的。 我们的一款即时MMO在设计就采用了这个思路。

Post a comment

非这个主题相关的留言请到:留言本