-
背景
如今的社交类游戏,大多采用分布式服务器架构,也就是说所有区服的数据存储在一个集群中,玩家可以无阻碍交互,一同游玩。
再谈游戏中的匹配,多数匹配玩法都属于在线匹配,属于非常实时的一种匹配,匹配节点会根据玩家的操作(开始匹配,报名, 匹配完成等) 动态插入删除。这也就保证了匹配池的节点数量会维持在可控的范围内。
而如果游戏中有海量的小团体, 要求每隔一定周期为全服所有团体分配一个实力相近的对手, 应该如何高效实现这个需求呢?
-
需求分析
根据需求描述,结合现有的团体功能在服务器中的架构,系统所需要完成的功能如下图所示:
简而言之,我们需要通过一定的规则把游戏中的所有团体两两匹配在一起,然后逐一通知自己的匹配结果。
对于海量数据节点的匹配,很显然内存中已经无法存下所有节点了。最直观的解决方案就是将所有匹配节点的关键信息存储在DB中,到达匹配开始时间后再每次获取一批匹配节点分批进行匹配。
确定好大致方向,接下来需要解决如下五个问题:
- 如何维护匹配节点
- 如何分批匹配
- 在何处进行匹配
- 匹配规则的具体实现
- 如何分发匹配信息
-
数据维护
匹配节点的维护很简单:因为匹配的范围是全服所有团体,很容易想到匹配节点的生命周期和对应团体是绑定在一起的,即
- 团体达到玩法开启等级插入一条匹配节点记录;
- 团体解散删除这条记录;
- 节点中关键字段变化更新记录;
值得一提的是,匹配节点的更新可以通过增加脏标记的方式,控制频率固定时间间隔更新, 这样可以避免其他业务代码影响到匹配系统的整体压力。
-
分批匹配
分批匹配,最重要的就是解决分批获取表格中全量数据问题,常见的做法有两种:
1. 遍历表格,按固定顺序固定数量获取数据依次匹配。
2.自定义哈希规则为每个团体分配一个额外的值随机的key, 对这个key建立索引,最后每批选择其中一部分key进行拉取。
表格遍历是最简单有效的方案,可以完成基本需求,但会面临两个小问题:由于是固定顺序固定数量下的分批匹配,每轮匹配的分组大致是固定的,这也就意味着极限情况下,可能出现多轮匹配到相同对手的情况。
另外,表格遍历数据返回的方式是单向通知,而不是主动拉取的。考虑到匹配是一个非常占用CPU时间的操作,在匹配节点非常多的情况下,前一批节点还在占用CPU进行匹配中而后面几批节点已经返回,从而导致缓冲区占满而丢失部分消息。
第二种方法实际上就是通过part key进行批量拉取,先不谈BatchGetByPartKey这个操作本身很低效,虽然解决了遍历表格出现的两种问题,但极度依赖哈希算法,随着节点数量的增加,key的数量也需要随之增加,也就是说哈希算法需要做到随着节点数量的增加动态变更,流程代码实现也将变得复杂,整体很不灵活而且低效。
那么有没有一种分批拉取策略既可以结合两者优势呢,又可以避免两者已存在的坑呢?
仔细斟酌,实际上我们需要的是一种可以随机顺序主动拉取的遍历表格方式。如果可以打乱整张匹配节点表格,再进行遍历拉取一切就很好办了,但如果想在内存中存下整张表格随机后的顺序则需要耗费大量的空间。 考虑一个折中的方案: 将整张表格的所有节点分成固定数量的X组,再将这些分组采用O(N)洗牌算法打乱, 最后每批拉取Y组进行匹配。这样,在内存中我们仅需存下分组的顺序以及当前拉取的进度即可。
有一点值得注意的是, 因为采用的是设置下标的分批拉取,在拉取期间如果发生了节点删除,拉取后续节点可能出现遗漏