和学霸一起学 | 游戏服务器的那些事儿

腾讯游戏学院 2021-08-10
游戏程序开发有两个大方向,包括前端和后端。其中,前端是指客户端方面,包括PC、手机和平板上面的可视化图形技术。后端则偏向于服务器,即用户不可见的部分。

本文将通过游戏服务器的起源、功能特点、分类和发展历史,以及系统架构、开发核心技术和难点、设计原理等方面来介绍后端的服务器技术。

#01游戏服务器概述

概念及起源


游戏服务器,是服务器硬件和软件的结合。其中,硬件负责承载游戏服务,软件则提供游戏服务。

游戏服务器的出现,是因为玩家对游戏有三个方面的需求。


第一,是游戏性的需求。

最早的游戏机,更讲究单局游戏的体验和过程,不可以存储进度。这种单局过关的游戏性,来自于用户体验的提升,来自于玩家对技能、关卡熟悉程度。随着用户体验时间的增长,这类游戏对玩家来说会变得容易,游戏性就会被消耗掉。

随后出现了一些可存档的游戏。比如,红白机上有了最早的可存盘游戏——《三国志:霸王大陆》。网络刚开始兴起的时代,PC端的游戏也是可以存储一些进度的。但当时的网吧基于安全需求,在玩家使用电脑后重置机器,游戏内容就会被清空。从游戏性的需求来看,玩家希望换电脑也能够继续游戏进度。为了解决这个问题,设计者就需要把玩家的数据和剧情放到网络服务上。

第二,是多人联机的需求。

单机游戏有它的乐趣,但更多时候,玩家与玩家之间的互动可以产生更多变化。比如,斗地主这个传统游戏,一共只有52张牌,三个人玩却可以有无数种玩法。为了实现这个需求,一般可以用中转的电脑来转发用户的数据和状态操作。这个操作不局限于用服务器来实现,在局域网上,一些互相转发的方式也同样可以实现。

第三,是保证游戏公平性的需求。

通常,游戏防止玩家作弊的方式就是引入第三方电脑裁判。

上述三个需求驱动了游戏服务器的诞生,它们同样也是游戏服务器的主要功能。此外,游戏服务器还有一个重要功能——充值。

功能


游戏服务器的功能,主要有四个。

(1)包括终端网络连接的建立和会话维护在内的网络服务。

有了终端和前端间的网络连接,才能实现前端后端的交互。而会话维护,主要指用户的登录和登出,同时也是该用户在服务器上的一个生命周期。会话维护还包括断线重连,这一问题在PC端不太容易出现,但在手游端会经常发生网络的切换(WIFI和WIFI、4G基站之间),这些都会导致玩家网络掉线。会话维护使得网络掉线后,玩家在服务器上的生命周期也不会消失。

(2)用户数据的存取

数据读取,一般会发生在以下两个场景:一是登录。玩家登入服务器后,服务器会从存储读取。二是切换。比如,从大厅服务器切换到战斗服务器,也有可能从DB(database,或者文件系统)读取用户数据。

而一般的服务器技术采用三种存储方式:

① 用户下线存取。用户下线时,服务器会对数据进行回写。

② 定时回写。比如,每隔半分钟、每隔五分钟,把数据回写一次。

③ 重要数据立即回写。比如游戏里,玩家打了一个特别贵重、极其稀有的装备,或者涉及金钱交易的数据,都是会及时回写。这种方式是为了解决前两种存储方式可能导致的问题:如果异常掉电,那么服务器会出现异常,导致用户数据丢失。

(3)游戏的逻辑计算。

采用服务器技术后,一些以前单机上实现的前端计算,被放到了后端。这也是为了防止作弊,以及解决以前存在的一些问题。比如,在FPS(第一人称射击游戏)中,玩家是否命中的判断是在前端,但命中后产生的伤害,包括护甲、免伤等数据,都要通过逻辑计算。以前的逻辑计算都是在客户端进行,不可避免地会有作弊的可能。如今,逻辑计算都在服务器进行,减少了作弊的可能性。

(4)是用户行为或状态变化的同步。

在网络游戏中,玩家在用户端的视野范围内,可以看到其他玩家或怪物、NPC(非玩家角色)的动作行为。这种真实世界的模拟,就需要各角色行为和状态的同步变化,这就是后端服务器的功能之一。

特点


游戏服务器虽然是互联网服务的一种,但相对互联网服务有一些自己的特点:

第一是,延迟敏感。延迟指玩家操作动作、发出指令,和动作在画面上呈现图像反馈到视觉,之间有一定的时间差。

第二是,实时的高强度交互。单次请求会产生很多服务器的逻辑,还有一些广播和同步。服务器承受的最大压力的逻辑是移动同步。比如,假设玩家在一个20人左右的场景里,每移动一步,客户端1s之内大概会发出5个移动包(假设),表示玩家方向或移动目标点的改变。对于其他用户来说,服务器也要实时同步,相当于消息量会放大20倍。这对服务器来说,是很高强度的交互。

第三是,业务逻辑复杂,内部耦合度高。由于游戏服务器具有延迟敏感和交互高强度的特点,这决定了服务器的状态维护是一个必然的过程,也导致了逻辑的复杂性。举例来讲,玩家击杀一个怪物,在服务器端,逻辑不仅仅是击杀一个怪物,也可能会触发一个任务,比如玩家今天要击杀15个怪物,这样类似的逻辑。

第四是,变更频度高、幅度大。手游时代,游戏的更新频率非常快,内容非常多。因此,服务器的更新和灰度部署要求会更高。

#02游戏服务器的发展

网络游戏发展史


上世纪七十年代末,网络游戏最早是文字MUD和UNIX网络同步出现。

之后,八十年代末到九十年代初,局域网连接的游戏出现,最早是《沙丘》,其次是《红警》、《魔兽2》和《DOOM》。

到九十年代末,出现了Diablo(《暗黑破坏神》)战网。

1997年,金山发布了一款比较有名的RPG类(角色扮演)游戏《剑侠情缘》。当时,《剑侠情缘》还是单机版的。后来在2003、2005和2009年,分别发布了网络版的1代、2代和3代。

1998年,主打休闲游戏的联众游戏成立。

2000年左右,MMOG(大型多人在线游戏)《传奇》登陆中国。

2004年,《魔兽世界》(WOW)出现。

2006-2010年,手游兴起。莉莉丝游戏做出了一个比较成功的手游产品——《刀塔传奇》。

2010至今,就是休闲类的《纪念碑谷》、网易次元文化的《阴阳师》,再到如今大热的MOBA类(多人在线战术竞技游戏)的对战手游《王者荣耀》。

游戏服务器的发展


第一代游戏服务器,是比较简单的单进程、单线程模型,无阻塞。服务器在接收到玩家的消息后,把消息按照队列进行异步的序列化。


这种单线程的服务器模型,最早的是类似于78年的MUD游戏。使用终端模拟程序telnet等,就可以直接登录。进入场景之后主要采用文字叙述的方式,即服务器告诉玩家在什么场景、有什么数据,然后玩家输入指令。早期的服务器,虽然没有很强的逻辑,但因为整体构架比较简单,所以承载能力较低。


第二代游戏服务器,为了增加并发,在服务器端也发展了多线程和多进程的模型。在服务器本身的逻辑上,开始增加了分区分服的结构。


比较典型的游戏是上图中,EA(美国艺电公司)旗下公司在1997年发布的《Ultima Online》,即《创世纪》。它是世界上第一款图形界面的大型多人在线角色扮演游戏。


此外,还有联众在2000年推出的休闲棋牌游戏。当时,联众跟地方的运营商(电信、联通等)合作,搭建游戏服务器。所以在游戏中,会有南方电信1、北方联通1等分区。这体现了第二代游戏服务器架构典型的分区分服特点。


第三代游戏服务器,相对更百花齐放一些,主要有四种类型。

第一种类型,是在第二代的基础上发展而来,实现三层架构的游戏服务器。

首先,Gate server作为所有玩家的游戏连接,主要负责处理网络的IO部分。然后,通过内部的网络即IDC(互联网数据中心)专网,与游戏逻辑服务器相交互。最后,通过mysql或其他数据库,把用户数据存储到DB server。它的主要特点是,把网络和存储IO进行逻辑分离,可以对每一层进行扩展。一般负载的瓶颈大都在GameServer层,这层可以做更多的扩展,从而支持单区单服增加承载的能力。


2003年的QQGame,是比较典型的三层架构服务器代表。它当时是腾讯首款自研的游戏产品,最高达到过PC端八百多万人在线的成绩。QQGame很快就超越了联众,即使在现在的休闲棋牌市场上,也处于绝对领先的地位。


第二种类型,是服务器集群,上图是比较简单的示意图。

首先,也有与玩家连接的接入类服务器,这里命名为GATE,它负责处理专网连接,在游戏WORLD处理游戏逻辑。然后它会把通用逻辑和功能抽取分离开来,比如管理、聊天、交易以及一些组队和工会,放到独立的地方,再通过Cluster服务器集群的方式提供服务。采用这种分组以后,以前同步的逻辑直接本地调用一个函数,来处理组队和工会,会变到另外一个服务器上,中间就多了一些异步交互。异步交互存在状态维护的困难,所以这种结构开发难度会更高。


MMO和RPG大都是这种结构。其中,比较有代表性的是最早期的《传奇》。它是一个经典的MMO,是盛大引进代理的韩国游戏。


《QQ幻想》和《QQ华夏》,也采用了这种结构。《QQ幻想》是腾讯在2005年,自研的一款Q版卡通类RPG,最高的时候有六十万左右玩家在线。《QQ华夏》则是07年深圳网域公司开发的RPG游戏,当时腾讯做代理运营。


第三种类型,是无缝地图。它的特点主要有三:

① 一个场景服务器只负责一部分场景;

② 玩家进入游戏后,没有加载地图的过程;

③ 在场景边缘处,数据可能同时存在于多个场景服务器中。此时会由world级的服务器进行管理,决定哪个服务器做决策。在边界切换时,不同场景服务器共同持有玩家的数据,玩家就不会感觉到明显的切换。最典型的,就是《魔兽世界》对无缝地图技术的应用。


第四种,是房间型服务器。

图中间的部分是大厅接入群,类似于上文中的gate和游戏服务器。这部分主要负责用户的接入、主要的逻辑处理以及其他一些大厅服务,比如《王者荣耀》和《英雄联盟》的5v5匹配。当很多玩家同时申请进入游戏时,房间型服务器会根据规则挑选出等级接近的玩家,分别组成队伍。之后单局的过程,叫做战斗接入群或房间服务。战斗接入群会做一个分布,比如,根据北方的联通和南方的电信,对用户做就近的接入。再如PUBG mobile,它的大厅集群放在北美,同时在北美、亚洲和欧洲都有战斗接入群,方便各地用户接入。


房间型服务器的游戏比较多,包括《英雄联盟》、《王者荣耀》和《刺激战场》(《和平精英》)等。

#03 核心问题、技术和实现难点

核心问题


游戏服务器要解决的核心问题,是满足海量游戏用户稳定和高质量的服务需求。

首先,“海量”指的是,十万到千万级的PCU(Peak Concurrent User,即最高同时在线用户)。此外,还有个名词DAU(Daily Active User,日活跃用户),是游戏运营的数据指标,而PCU主要考量服务器的性能数据。

其次,“稳定”代表着:

①服务器不能宕机,一旦服务器出现问题,用户的直观感受就是掉线了。

②要容忍弱网络的问题。比如,用户网络不稳定,连接断掉之后,服务器可以允许用户在短时间内进行WiFi和4G的切换,允许用户重新连接,保证服务可以继续。

最后,“高质量”主要是指处理网络的延迟和一些逻辑问题。

核心技术和实现难点


游戏服务器的核心技术和实现难点主要有四个:

①  单服处理能力

对于不同类型的游戏,单服即一个服务器,支撑的在线玩家数量是不同的。休闲类如棋牌游戏,可以支撑两万到三万的玩家;MMO可支撑的玩家数在五千到八千;而射击类等高强度交互的游戏,只能支撑两千到三千的玩家。

②  逻辑耦合度高

服务器有很多集群的设计,所以在单服处理能力方面,会有算法的优化。比如,排序方面有排序查找、hash(哈希,“散列”算法)和二叉树;还有用空间换时间的方法,把经常用的数据放到内存里,减少CPU的经常读取。

服务器设计比较难的地方在于逻辑设计。很多逻辑是一个整体,要想将它分离开,就要找联系相对薄弱的地方。举例来说,组队和游戏逻辑的相关性比较小,就可以抽离出来。有些服务器单独将组队剥离,那么组队的消息接口和逻辑,就容易抽离成另外一个独立的服务器。但即使逻辑之间可以分解,这也是有限度的。分解的越细,异步协作和状态维护的内容就会越复杂,游戏开发的难度就越大。

③   延迟敏感

在防止延迟上,前端和后端需要一定配合。比如,前端会提前按帧缓存,当出现网络延迟和卡顿时,游戏其实还可以继续进行一段时间,这样玩家对延迟就没那么敏感。但是,提前缓存会存在玩家手感差异的问题,而且也不能缓存太多,否则玩家感受到的延迟就会比较明显。

④  版本更新

现在,手游时代很少会停机更新,即使有也大都在凌晨。更流行的方式是不停机更新,这就要求逻辑、资源一定要做程序设计上的分离,保证在更新资源时,程序可以重新读取并加载,且不中断。


上图是游戏服务器的一个系统结构示例,它偏向于房间型结构。首先,前端采用TCP或其他协议进行接入;之后,大厅接入群,主要包括接入服务器和游戏逻辑服务器,通过Proxy或者哈希算法的方式,把DB(database,数据库)做分布式存储。因为一台DB存储不了千万上亿级的用户数据。最右侧,是一些内部的接口。举例来讲,如果运营商要发布活动如经验加倍、金币加倍等,就会通过管理端向游戏逻辑服务器做接口服务。图中的战斗接入群,主要处理单局的游戏内容。

此外,在公共平台还有支付、关系链和排行榜等功能。支付与游戏逻辑的相关性不大,可以独立出来。支付的大致方式有两种:一是将人民币转换为游戏内代币,二是代币旳消耗。关系链一般是第三方平台的关系链服务,包括QQ关系链、微信关系链和Facebook关系链等等,有些服务通过第三方的API或接口进行同步。排行榜也可以做成一个独立公有的服务器。


游戏服务器的网络协议主要有Tcp、Udp、R-udp和http四种。

Tcp是面向一个流的模型,本身有滑动窗口重传的机制。协议没有边界,但有流量控制,丢包会影响它的传输速度。

Udp是点对点的,没有可靠性的控制,不能保证消息包一定被收到,丢包会影响传输的成功率。

R-udp是在Udp的基础上,吸收了Tcp的特点,保证了可靠重传的机制。

http一般用于拉关系链或web端发布接口到游戏侧做活动的场景。


上图介绍了游戏的接入逻辑。手游玩家在开启游戏时,登录后的第一个界面就是选区,所有区的列表,都放在目录服里。目录服的实现方式有两种,一是将目录服都放到客户端,二是目录服放在单独的服务器上。若采用方式一,当选区增加时,客户端就需要更新。所以,大部分的目录服都放在单独的服务器上,列出有哪些区,这样即使选区增加客户端也无需更新。

一般来说,各个逻辑服务器会向目录服上报各自的服务状态。当用户登录时,目录服首先就会回复一个服务器列表,客户端可以看到游戏的各个选区,如一区、二区等。在这个过程中,目录服会尝试各个服务器连接的响应速度,最后选择一个最近的登录服,登录到大厅。


在地图服务器的技术中,视野同步最常用的是九宫格技术。九宫格中的一块格子,比屏幕略大。服务器一般会认为,以玩家所处格子为九宫格的中心,整个九宫格的其他玩家和怪物都是玩家关注的对象。当玩家在格子内移动时,玩家的视野不会发生变化。但如果玩家移到其他格子中去,视野就会发生变化。比如图中的例子,若用户由B3移动到C2,则视野范围会由紫色九宫格变为黄色。由于视野范围变化不是很大,服务器不用立即加载玩家的同步数据,除C2之外,其他格子中的任何行为都不会同步给玩家。九宫格技术保证了用户在行走中的平滑感的同时减少了交互的次数。

地图管理,包括动态阻挡和静态阻挡。动态阻挡,指两个玩家同时到达一个地点,服务器是否能找到平衡点来同时显示两个玩家的位置。静态阻挡则指,静态的物件放在某处,服务器要判断玩家是否能走过去。比如遇到一堵玩家走不过去的墙,那这堵墙就是静态阻挡。

移动同步,主要需要解决的问题是网络延迟导致的拉扯和平滑。平滑主要是利用数学上的插值技术,将两个差异的地方在几帧之内快速平滑地拉过去。


游戏中的商品购买服务,可以看作是一个异步状态机。当客户端的用户提出购买请求后,购买服务器提出预扣费请求,将信号传递到货币系统服务器中,同时添加物品记录到DB服务器。在记录添加后,再次返回货币系统服务器进行扣费确认,并返回到客户端,完成整个购买扣费流程。这样的异步预扣费机制,是为了防止中间环节出现故障后,导致扣费未发放商品或商品已发放但未扣费的问题。


在DB层的数据存储,一般采用分布式技术。现在的游戏基本都有上千万到亿级的注册用户,庞大的数据量决定了一台服务器不可能存储完全。数据通过关键值key值,传输到路由表中,进而传递给DB层。key值一般是内部生成的用户ID,采用哈希值的方式把数据散列开。

举例来说,有十台DB,100号玩家,那么玩家号除以100,得到的尾数是几就放在第几位,从而把数据散列开。采用哈希值的方式,还有一个固定规律,即存写时都是相同的规则。

路由表有两种方式,一种是直接存在数据发起端,另一种是放在独立的proxy服务器上。


游戏服务进程的框架,和大部分服务器是类似的。都是初始化、退出以及中间的循环处理。循环处理包括网络事件、定时器处理、网络消息处理、事务处理和消息队列处理。网络事件指连接和断连;定时器处理,是通过定时器的计时,来定时保存用户的数据和一些定时逻辑;网络消息处理,是网络上的收发包;事务处理,是服务器产生一个逻辑的时候保存的一些中间状态;消息队列处理,则是在其他逻辑模块发送业务请求时产生的。


中国的外挂问题,相对外国更加明显一点。而韩国的政府对于游戏产业的支持态度,使得防范外挂相对容易,所以很多韩国游戏引进到国内后,就面临着水土不服的外挂问题。外挂,包括键盘鼠标模拟、注入、协议破解和程序破解等方式,还有最难的完全模拟客户端的方式。

反外挂的核心,以服务器逻辑为主。当逻辑放在服务器上时,外挂没有机会。不过这种方式的成本也很高,其一是因为这个逻辑会加重服务器的负担;其二是,逻辑放在服务器上,反馈到客户端时,会导致游戏体验的延迟,影响玩家的游戏体验。常见的反外挂手段,包括动态协议栈、验证码检查、加壳、动态特征码检查进程和法律等方式。

#04 设计原理和方法论简述

设计原则


设计原则最首要的是KISS原则,即Keep It Simple,Stupid。系统的设计要尽量简单清晰,不要太复杂,减少模块的耦合,要做到让玩家一看就懂。


其次,是灰度方面的设计。

第一,要柔性可用。柔性可用主要指,当服务器条件有限不能提供完整服务的时候,可以用柔性的方式提供相对优质的服务。比如战斗服务器出现故障时,不能打PVP(Player VS Player,玩家对战玩家),那么要能够打PVE(Player VS Environment,玩家对战环境)。

第二,是在不同场景下提供不同的服务。服务器要保证系统的核心服务是一定可用的。而非核心的服务,如排行榜就可以暂时停止,不能因为非核心服务,而阻塞整个游戏的服务进程。

第三,服务降级和灰度分布。当一些服务出现有损的情况时,可以做服务降级。排队就是服务降级的一种。当玩家人数超过服务器的承载能力时,服务器就可以通知后面的玩家服务器繁忙,来进行用户的排队。

可行技术及工具


已验证可行的常见具体技术,包括:

①无锁通信队列。两个进程在后台线程通信的时候,队列如果只有一端读、一端写,那么是可以不用锁来实现的。该技术最早应用于Linux底层,现在已经比较常见了。

②异步模型。异步模型在服务器技术上应用的比较多。大部分服务器采用Cluster技术时,都会采用服务器协作的方式,那么很多的同步调用就会变成异步的交互。

③通用框架。通用框架包括big world框架和网易大神——云风开发的skynet,这两个框架都比较成熟。skynet的特点,是使用lua(一种脚本语言)。

④Key-value数据库产品或内存数据库。以前的服务器数据库,用的是Mysql等传统关系型数据库。现在主流使用的是Key-value数据库产品,如Redis(Remote Dictionary Server,远程字典服务)和MongoDB(基于分布式文件存储的数据库)等。Key-value数据库大部分是根据一个用户ID,就可以得到用户所有的数据。


关于工具,首先是调试器。Linux下,著名的gdb调试器计算机开发一般都会用到。Windows下,一般使用Visual Studio自带的调试工具。


关于内存泄露的检查,Linux下常用的有valgrind,Windows环境下,则有Leaks Detector。


上图展示了一些性能分析工具。在Linux环境下,一般使用gprof,具体用法可以参考网址 http://sourceware.org/binutils/docs/gprof/ 。Windows环境下,使用visual studio自带的profiling。此外,还有第三方的工具,如Truetime。


网络抓包工具,是在网络开发中用得比较多的工具。Linux下,最常使用的是Tcpdump。Windows下最常用的是wireshark,它的优点在于,可以直接与网卡进行数据报文交换,同时具有语法过滤功能。

此外,关于静态代码检查,最常见的工具是PC-Lint。

发展现状及前景展望


游戏服务器相对于之前已经发展了许多,而且计算机的综合能力也越来越强。

首先,游戏服务器已进入多核时代。目前正在使用中的PC服务器,最多有六十四核,加上超线程,总共能达到128个核心。

其次,编程语言发展比较快。在C语言之后,Java也有很大市场,用Go的人也多了起来。欧美的游戏,很多用Java做服务器开发。此外,《和平精英》服务器用的是C和lua结合,lua为主写逻辑。lua的性能虽然稍微差一点,但有最大的优点:热加载更新快,开发效率高。

然后,有更真实的游戏世界。游戏的真实性,更偏向于前端设计。虚幻和CryEngine这两个大型引擎,使得游戏对于真实世界的模拟变得非常强劲。不过,这两种引擎都比较重,而Unity引擎是比较轻量的。

最后,游戏服务器会具有更实时的交互和更快的响应速度,做到所见即所得。

好的游戏体验,离不开服务器的开发设计,随着科技和游戏设计的相互促进、相互发展,用户将会获得更加优质的体验。

来源:腾讯游戏学院
原文:https://mp.weixin.qq.com/s/sF0sExm6cppIYe7SRBfa9A

最新评论
暂无评论
参与评论

商务合作 查看更多

编辑推荐 查看更多