财新传媒
位置:博客 > 盐科技 > 七游CTO吕游:自研服务器需要注意的四大问题以及如何稳定运行

七游CTO吕游:自研服务器需要注意的四大问题以及如何稳定运行

自研引擎,考虑的不仅仅是技术,还有人的因素,甚至人员因素比技术更加重要。


成都——“当初我在设计这个服务器的时候每一步是怎么思考的,是怎么样去设计它的,我希望我讲的这些东西中,十条里面有一条能够提供一些参考,基本上目的就达到了。”成都七游科技有限公司CTO吕游在前段时间盐科技主办的盐铁第3期的活动中讲到。

现在很多手游研发团队,除了少数团队是自研引擎外,更多的则是使用Cocos2dx、Unity3d和AIR 。而吕游他们则是少数用自研引擎的手游研发团队,吕游在活动中就他们自己写的服务器给大家做了分享。

为何选择C++

吕游在考虑设计整个服务器的时候,他说,作为一个CTO,考虑的不仅仅是技术,还有人的因素,甚至人员因素比技术更加重要。

他认为,CTO在团队里的技术是比较高的,但是你下面有很多执行人员,就要考虑他们的技术,不管是项目初期、中期还是最后,始终都要考虑到整个技术团队。

考虑到团队里有几个是开发C++的程序员,加上吕游也喜欢,所以他选择用来研发自有引擎。

说到为何在选型的时候不选择有脚本的结构,吕游解释到,他之前用过很多服务器,各种脚本的都见过,但是大多数都有一个问题,就是效率。有的脚本的服务器效率是比不上C++的。

他还说到调试问题,“在VisualStudio里写代码的时候,一个C++的东西,你很容易从Assist里面得到很多提示,然后你在调试器,中间工具里面,还有其他各个方面,C++的支持率是最完善的。”

接下来吕游会考虑负载能力,他说其实不用考虑特别高的负载,在网上看多有说万人在线的说法,其实大多数负载并没有那么高。很多人会把目标定在5000人,他认为对于一个正常的页游和手游来说是能够满足的。

还有一点就是考虑快速开发的问题,因为大多数手游和页游都属于开发周期非常短的产品,在考虑技术的同时必须要考虑项目的进度,产品从无到有只有几个月的时间,怎么样选择一个可以快速开发的技术。

他们最后选择了单线程纯C++的结构,吕游在这之前咨询用单线程C++服务器的朋友,朋友开发的是大型多人在线的游戏,承载力在4000人以上,没有一点问题。

“它不考虑同步、锁定等多线程会遇到的麻烦问题,也不考虑脚本,这在开发过程中是比较方便的,我们在综合运营成本和维护的考虑下,决定:日常开发主要在windows下进行,而成品上线则是linux环境。”

服务器的基本功能

在决定方向后,就要厘清基本的结构,因为要做快速开发,吕游把功能大概分为:实体对象存取和SQL数据库,主要负责数据的存取;静态数据表,就是策划的数据;双向RPC和广播支持。他还讲到,需要快速高效的开发,他们在上层会有一个异步调用的流程。

数据库:

为了支持项目比较快速的开发,吕游他们要解决一些最基本的问题。在数据库里,实体对象的存取,他们通过一个XML来定义一个对象的结构,他们直接把对象序 列化,做成一个二进制的数据包,整体存到数据库里。整体存,就涉及到版本管理的问题,因为你服务器更新的时候,你对象结构会变,所以我这里就说在每一版 本,不同的定义之间我还有一个版本管理的问题。

SQL的支持是什么?吕游他们开发的服务器支持直接到数据库里面去执行SQL语句。写SELECT、INSERT之类的,可以直接写,也可以自己建表。这 一部分主要是为了方便和外部的系统接口,因为外部的有一些系统,比如充值,连上他们接口其实最简单最方便的就是通过数据库来进行数据交换。

网络:

网络是怎么设计,吕游说他们看了很多服务器设计,做的不是特别好。他认为作为一个高效的开发的服务器一定要有一个RPC的机制,开发人员从网络协议中解放出来,专注到逻辑。

吕游说到他们的RPC是一个强类型的,想调用一个RPC函数的时候,不会用一个字符串ID去搞这个事情,会直接调函数,当我的RPC定义了一个AddPartner的时候,可以看到它是含有一个Role参数,经过工具生成之后,就会有一个对应代码在客户端。

然后在客户端怎么用?吕游说到,客户端有些情况不止在一个点去使用这个东西,所以他在客户端会有一个Global的RPC,如果需要接收这个调用的,可以 直接写一个Add Partner回调函数,然后在初始化的时候绑在这个函数上,所以服务一旦Add Partner,就可以收到这个消息,得到一个对象。

还有广播,吕游说如果在逻辑里去过度关注广播,会让代码变得很乱,效率变的很低。为了解决这个问题,吕游根据不同频道,给Clients这个通道附上不同的代理:如果是全服广播,会有一个专门的All Clients;如果是有公会,就专门在服务器上建一个频道。

最后会有不同的广播,单播和多播通道,这些通道最后附到变量上以后,就可以像刚才那样去调强类型的RPC,调用会直接发到网络去,一份数据会直接发到网络 去,有专门的网络线程负责去编译下面的这些Socket。这样在管理这些客户端的时候,会觉得特别方便,而且它的负载率也很低,因为把负载拆分到了网络线 程里面了。

静态数据表

然后静态数据表,为了方便策划Excel数据的导入。吕游把Excel直接想成数据库的形式,他们的数据结构层次不是特别多,基本是平面的二维表。

那么如何处理它呢,吕游同样用XML去定义,会指定哪个文件的那个表格去从生成程序里要用的名字,一般来说都会生成一个实体类的数据,然后这个部分会拆分成好几个东西。

吕游讲到这需要代码去读取它,服务器和客户端都需要,所以有专门的代码去访问它,在工具生成这个代码的时候,他们会有一个Index的设置,然后它会生成和index相匹配的一些查找的方法,就很方便的通过这些查找得到需要的数据。

异步流程:
吕游说,其实做了那么多基础工作,他最关心的还是其他的程序员怎么去用这些代码,你面向的不只是玩家,还有你的程序员们,所以要设计一个好的方便的接口,来提高他们的日常开发的效率。

他举了一个例子讲异步流程,比如你要建一个数据库查询,还要等待1000毫秒,他会在这个流程里面,可以写,其实是排了两个异步的任务在那里,最后一个 Yield,这个流程就回去了。主线程的CPU空闲下来,可以做其他事情了。但是会有其他线程去负责数据库的查询,当这些事情执行完了以后,就可以得到一 个回调,在回调函数里就可以取到刚才的结果。

这样排列的代码其实从视觉上来说,它是一个比较连续的,但是中间又断了一下,为什么,其实断下来这个事情,除了语法上本身的需求以外,吕游还有一个基于人的考虑。

他说到,不会把一个负载特别高的程序设计的特别特别的简单,因为那样的话,你下面的程序会滥用它。

还有就是异步流程不需要额外的信息去保存,吕游举例到,假如你是一个连续的写法,一个流程下来,然后直接调用一个sleep,那么在sleep这段时间内,你这个函数的栈,还有它下面的栈,所有的栈都是被系统保存下来的,这就是开销。

“那么我这种写法是怎么样的呢?在Yield之后,这个栈已经没有了,我不需要保存这个栈,而如果要在两个流程之间,要去维持你的状态的话,你该怎么办, 我强迫程序员去请成员变量,他只需要是保存自己在两个流程之间最需要的东西。所以这样整个流程,他在等待期间的开销是非常非常低的,只需要保存确实必要的 状态。”

如何稳定  

吕游讲到在设计服务器的时候,首先是稳定,稳定压倒一切,在稳定的前提下要有足够的负载力。然后是做到如何快速开发。

如何让整个项目出错的风险讲到最低,吕游说到会尽量去把高风险的地方屏蔽掉,他不会让他的程序员自己去管理内存,也不会让他们控制这些对象。他会把那种长 期存在的对象用XML定义之后,会创建一个函数叫Create Instance,创建以后有智能指针,这个会让整个结构稳定性提高不少。

然后是使用成熟的库简化细节,像Boost,你可以在C++里面写Foreach来取代迭代器,一方面是清晰,一方面是避免出错。

吕游还特别强调说到,他们的服务器稳定的重要一点在于,内部调用少容错,多报错,做好直接挂掉。他希望在任何错误发生的时候在尽早的时候让服务器挂掉。

“虽然让服务器挂掉这个是听起来很可怕,但实际上对服务器非常有帮助的,在日常的开发中如果服务器老挂的话,一定会全力以赴去处理这个问题,它会强迫所有人去关注任何一个错误。”

所以一般来说,吕游在处理内部的调用的时候,尽量在初期会检查一些问题,一旦查出就把服务器直接挂掉,挂掉的时候可以抓住现场,能够看到挂到所有的栈,所有的局部变量等信息,能够很快根据这些信息修正错误。

他说到,“当时我们这个服务器上线的时候第一个月是非常痛苦的,随时都在出问题,基本上就会几个小时挂一次,但是随着问题一个个被找到,在大概调了有一个 月左右的时间,基本上服务器就非常非常稳定了,到现在为止的我们的服务器大半年基本上不用去管他,没有任何问题,只有在更新版本和机房维护的时候才会重 启。

那么在满足了高效稳定之后,如何去快速的开发。吕游说到,更多的是一些人员和工具方面因素,提供简单的方便的开发工具,然后从技术上来说,就是刚才讲到的类似于RPC的一些框架,一些数据库所有的这些机械的东西,都是用工具去生成的。

吕游还讲到,他们提供了一个自主在线的更新系统,主要是给策划用,策划自己提交数据,直接下载客户端,自助更新服务器,可以非常快的进行迭代,这个对项目的进度是非常有利的。

【盐科技www.yankeji.cn 微信yankejicn】

推荐 0