网络游戏服务器构架设计(二):刀剑Online - 连接负载服务器CLS

    本文并没有涉及什么逆向工程,只是拜读刀剑Online服务器端主程的文章后[1],想结合自己的经验谈一谈。

PS:由于题目范围太大,本系列的前言做了一些限制。

 

一、网络游戏服务器

    要想设计好网络游戏服务器的构架,首先需要知道网络游戏服务器在玩家游戏过程中发挥什么作用。就我个人的理解:网游服务器在玩家游戏过程中扮演上帝的角色。玩家在服务器制定的规则下进行游戏,服务器负责同步在线玩家之间的属性、操作、状态等等,最终在多个不同的客户端呈现一个“统一”的游戏世界。

    所谓的服务器构架在本系列blog中,主要是指如何将服务器各部分合理的安排,以实现最初的功能需求。好的结构不是一蹴而就的,是通过需求的推动一步步的完善。而且每个设计者心中的标准不尽相同,所以我认为并没有绝对优秀服务器构架。本系列文章中所谓的优秀构架是指各方面达到一种平衡(包括成本等的非技术因素)。

    下面先介绍刀剑Online的服务器构架(后续还可能有WOW、天龙等):

 

二、刀剑Online

image    

图1 刀剑Online服务器构架

 

    看了像素的技术总监魏华的文章[1],感觉有点意思。文章中所介绍的服务器构架并不复杂,但满足一般MMORPG网游要求应该是绰绰有余了。按照魏华自己的话,这样的服务器构架主要满足能够接受以下几条限制的网络游戏:

  1. 游戏同时在线人数在1w人以下。
  2. 服务器为多进程程序,可部署在一台或者多台机器上。
  3. 服务器内存足够大,一般一个进程1~2G的内存需求还是应该满足的,64位系统能支持更大的内存需求。内存这块主要看游戏的设计需求。
  4. 刀剑属于格斗性质的网游,对CPU和带宽有一定的要求。(这篇文章写于2005年,当时刀剑可能是按照256k、512k或1M ADSL的网速进行设计的。现在已经开始普及10M网,带宽和网速应该可以满足),网络延迟的问题本文后面再做展开。对CPU的要求主要影响可承载人数。
  5. 地图采用独立小场景的管理模式,各个场景之间通过传送点来连接。每个场景服务器程序分管一部分地图。

    服务器包括游戏中和游戏外两大部分,这里主要讨论游戏中的服务器构架,类似客户端自动更新等的游戏外服务器不在本文的讨论范围。

    接下来对刀剑Online的服务器构架的各个部分进行详细的分析,其中包含很多我自己的想法,很多内容都是我猜测的,因此不能“信以为真”。

 

2.1 连接负载服务器(Connection Load Server,CLS)

    游戏客户端在游戏过程中实际上是和连接负载服务器(简称CLS)进行连接并做数据交互的。如文中[1]所诉,CLS主要的作用是:

  • 把网络连接和真正的游戏逻辑隔离开,降低游戏逻辑服务器处理网络交互的负担,同时提高游戏的安全性。有了CLS,刀剑Online的服务器对于玩家来说就是一个黑盒,如下图:

image 图2

  • 使场景服务器(Zone Server)更为独立——客户端连接CLS而不是直接连接Zone Server的好处是:用户切换场景服务器时,并不会导致原来的TCP连接断开,从而使设计更为简单和独立。
  • 提高发送广播消息的效率,比如需要全服广播时,游戏逻辑服务器只需要对CLS发送一条广播指令,而向每个用户的广播工作由CLS完成。
  • 完成客户端数据交互的加密解密过程。

连接服务器的主要工作正如上述魏华谈到的,但有几点并没有做出强调,下面本人结合平时的实际工作给出一些补充(不一定正确):

根据经验:

    CLS作为与client建立连接、进行数据交互的“Gate”,从程序角度来看CLS的代码应该是最简洁高效的。因为CLS主要负责与客户端交互数据是的加密解密、以及数据搬运。而使用流加密算法RC4对整个数据流进行加密解密是很耗CPU的,因此代码的高效在这个模块是十分的重要。

    为了提高程序的运行效率,CLS程序往往会使用-O3的选项进行编译,这无形中又对代码的编写有更高的要求。CLS一般会有自己的打包机制(控制发送频率),因此常会使用TCP_NODELAY选项禁用Nagle算法。

    CLS常会被分配到不同的物理机器上,因为操作系统在处理TCP包时,需要通过软中断来通知进程或者唤醒system call,在服务器十分繁忙的时候CPU可能处理不过来。解决办法是:使用多核的服务器,然后把TCP的软中断平均分配到多个CPU。(一些操作系统默认只使用0号CPU来处理,Fedora Core release 2默认就是只使用0号CPU,较新的版本我没有做研究)

    CLS在做数据流的解密后,往往需要把数据包构造成内部服务器进程间通讯使用的protocol,这种protocol模块要独立,序列化和反序列化的接口要稳定,这样以后需要更换协议模块也不至于伤筋动骨。可以使用像google的protobuf这样的开源协议,减少开发难度。

    CLS负责建立和client的连接,多会使用多个CLS进程才能支撑1w的在线人数,因此在CLS前端一般会有负载均衡的程序,负责把建立连接的请求均匀的提交到各个CLS。

   有一个需要讨论的问题:作为服务器端的“Gate”,只负责数据转发的CLS是否需要对client发过来的数据进行完全的解密?或者只解密包头,知道转发的目的地即可?(RC4并没有增加流的长度,因此可以只做部分解密)

  CLS只做部分解密:

好处:将耗费CPU资源的解密功能分摊到别的进程;各进程各服务可以在解密后用不同的方案来构造自己的protocol。

  CLS做完全解密:

好处:可以提前过滤部分无效的消息,只做部分解密也可以做提前过滤,但是这样太过于依赖协议的设计;在CLS处做完全解密,则往后服务器端的之间的消息传递都是明文,利于抓包查错;由于CLS的功能比较简单,很容易通过加机器来进行扩展,因此计算放在CLS上是比较明智的选择。

 

总结

    CLS是一个功能相对简单但要求代码简洁高效的程序,在设计实现的时应该注重效率及代码编写规范。经过对比在CLS程序对数据流进行完全解密是利大于弊的,推荐使用这种方案。

 

 

2.2 ...................待续

    (2.2节及后续章节将写在后面的文章里)

 

 

References: 

[1]像素软件技术总监--魏华:《刀剑Online服务器构架分析》

posted @ 2012-08-20 20:48 ychellboy 阅读(...) 评论(...) 编辑 收藏