从腾讯QQ升级游戏之“快速加入游戏”功能的实现缺陷看C/S之间如何正确分配相关协作

转载:http://space.itpub.net/17007506/viewspace-615570

笔者在闲暇时,偶尔会登录腾讯QQGame玩玩升级游戏。这确实是一款非常优秀的软件作品,腾讯的开发人员在此展现了极高的技术水准。QQ游戏同时在线用户数都在百万到千万之数量级以上,可以想象其在性能方面所面临的挑战有多高。

    QQ升级游戏有一个“快速加入游戏”的功能,方便玩家尽快加入目标牌桌。这本身是个非常人性化的功能,但其实现却存在一个缺陷,当玩家当前所在房间内,同时执行“快速加入游戏”功能的用户数较多时,常常会出现加入失败的情况。笔者碰到的最糟情形是重复5、6次以上,才最后成功加入,其间获得的用户体验自然是不够好。

从腾讯QQ升级游戏之“快速加入游戏”功能的实现缺陷看C/S之间如何正确分配相关协作_第1张图片

    (图A)“快速加入游戏”失败示意

    注意:点击图片可以放大观看

    笔者分析这个缺陷的根源,可能是“快速加入游戏”这一功能之实现在服务器端与客户端之间的协作分配方式有问题:

    QQgame的服务器端维护了游戏大厅中所有房间、和每个房间中所有牌桌,及相关玩家在这些牌桌的占位情况;而每个登录QQgame的客户端会实时(因为客户端数目庞大,服务器端提供数据更新服务的实际延迟时间会达到秒级以上,并不能真正做到实时)地从服务器端获取这些数据;玩家(用户)进入某个房间后,当其执行“快速加入游戏”功能时,客户端会先分析当前房间中所有牌桌的占位情况,并选择一个未满桌的牌桌,然后向服务器端发出加入此牌桌的请求;服务器端收到客户端的加入指定牌桌的请求后,开始尝试完成加入操作,然而,因为客户端数据更新的后滞性,造成服务器端在此前可能已经收到其它客户端所发相同的(指定了同一牌桌)加入请求,并为其完成加入,使得此牌桌位满,于是本次加入操作失败。

从腾讯QQ升级游戏之“快速加入游戏”功能的实现缺陷看C/S之间如何正确分配相关协作_第2张图片

从腾讯QQ升级游戏之“快速加入游戏”功能的实现缺陷看C/S之间如何正确分配相关协作_第3张图片

    (图一)客户端执行“加入未满牌桌”

    显然,解决此缺陷最简单的做法便是改变服务器端与客户端之间的协作分配方式。原来的方式之所以造成可能加入失败,是因为进行“快速加入游戏”,必须先选择一个未满桌的牌桌,这必然依赖于当前房间中所有牌桌的占位数据,这些数据是由服务器端所维护的,如果在客户端完成这一选择,就需要从服务器端实时更新这些数据到客户端,而数据的更新根本实现不了真正的实时,于是有两个以上客户端同时发出向同一牌桌加入的请求就不能避免;那么,如果“选择一个未满桌的牌桌”这个工作不在客户端进行呢?我们可以由客户端向服务器端发出一个自由加入牌桌的请求,这个请求并不指定目标牌桌,服务器端收到请求后,直接找到一个未满桌的牌桌分配给客户端所对应的玩家,并把此结果返回给客户端;在这种协作方式下,无论多少个并发客户端同时发出加入请求,因为服务器端都是排队按顺序来一一加以完成的(可以通过对牌桌的玩家占位数据加同步锁以实现多线程安全来实现),所以总是不会出现争位而因满桌失败的情形。

从腾讯QQ升级游戏之“快速加入游戏”功能的实现缺陷看C/S之间如何正确分配相关协作_第4张图片

从腾讯QQ升级游戏之“快速加入游戏”功能的实现缺陷看C/S之间如何正确分配相关协作_第5张图片

    (图二)服务器端执行“自由加入未满牌桌”

从腾讯QQ升级游戏之“快速加入游戏”功能的实现缺陷看C/S之间如何正确分配相关协作_第6张图片

    (图三)“加入游戏”场景实现的参与类视图

    如何在服务器端与客户端之间正确分配相关的协作,是分布式软件设计中极其关键的环节。由于网络的延迟特性,我们不能将分布式环境下的数据与桌面应用下的等同看待;QQ升级游戏的开发人员有可能忽略了这一点,而没有更深入地思考这一协作分配问题。有个所谓“客户与服务器端协作分配之数据依赖”原则应当尽量遵守--如果某个功能的实现依赖于某些数据,那么这个功能的实现最好分配给数据的直接拥有者(不论是客户端还是服务器端),这实际上就是GRASP之信息专家模式的翻版(GRASP模式中数据的拥有者指的是对象)。

    当然,因为QQGame在高性能方面的要求极高,QQ升级游戏开发者可能有其它的考虑因素而选择目前的做法。例如,每个牌桌在开始进行游戏时,有复杂的游戏逻辑要运行,这些游戏逻辑如果都放在服务器端运行,恐怕需要的服务器集群是个吓人的数目,腾讯或许不愿意投入如此多的资金去购买这些服务器;那么玩家的机器同时充当客户端与服务器端的做法就是一个很自然的选择。游戏开发者可能会这样设计:第一个加入某个牌桌的用户,其主机将自动充当本牌桌的游戏服务器,此后,其它玩家要加入此牌桌,其加入请求应当发往第一个加入的用户主机,而非腾讯的服务器。不管怎样,这些都涉及到极其复杂的分布式架构设计问题,有兴趣的话,笔者将在后续文章中进一步深入探讨这些问题。实际上,升级游戏在可以执行“快速加入游戏”功能前,还有一个进入某个房间的步骤;而同样由于数据更新延迟的因素,造成玩家常常选择加入一个当时显示人数为未满的房间时,结果却为已满的情形。要强调的是,这一问题是无法通过上述调整协作分配的方法来解决的。

    为了改善用户的操作体验,避免加入游戏失败的次数是必要的,实际上,除了上述的做法,还有其它临时性的修复方法。例如,可以增加一个所谓“自动快速加入游戏”的功能,就是客户端自动重复执行原来的“快速加入游戏”的功能,直到加入成功为止(不需用户重复按那个按钮而已,一笑)。另外,还有一个减少“快速加入游戏”失败概率的方法,就是客户端在选择目标牌桌时,不援用固定的策略,而是使用某种随机方式,这样多个客户端同时指定加入同一牌桌的概率大幅降低,争位失败的情形自然也大幅减少了。当然,上述两种途径还可以结合在一起应用。笔者很期待腾讯能尽早修复上述缺陷,给包括笔者在内的玩家们一个更好的操作体验。

你可能感兴趣的:(C/S)