从零开始编写网游服务器-第一篇:服务器设计

从事游戏开发已经有几年了,虽然平时的工作以编写游戏逻辑为主,但是工作之余也看过不少关于网游服务器的文章和源码。一直没空整理出自己的想法。刚好最近开始接触公司的服务器引擎,使用过程中有很多不习惯的地方,所以,我打算借此机会,梳理下我的做法。如何编写一个网游服务器。
说明:这里的从零开始,并不是说什么都是自己从零编写,比如网络库。我的原则是,如果有稳定可靠的第三方库可用,坚决不重复造轮子。
网游服务器是一个典型的CS架构。服务器主要的功能如下:
1、维护客户端链接,接收客户端请求,发送服务器反馈给客户端等
2、为游戏提供数据存储服务
3、处理游戏业务逻辑,实现一些基础管理如玩家管理,副本管理等,以及一些高级玩法,如工会、家族等
所以,服务器需要些什么?
1、网络层。
无论你采用最原始的socket阻塞调用,还是epoll、iocp等技术,服务器对于网络,关注点一直集中在3个方面:
a)有新的链接加入/链接断开
b)读数据
c)写数据
这里不讨论该用什么样的网络技术来提高效率。因为我对网络编程也知之甚少。但是对于一个网络库,只要在这3个事件发生的时候能通知到游戏服务器,无论是事件通知还是函数回调,都能满足服务器的需求。而网络库的效率,则取决于他的是实现方式。
如果是端游,支持TCP即可。如果是手游或者页游,可能还要支持websocket、socketio、http等。
为了方便开发和测试,这个网络库最好还是能在linux和windows上运行。我们一般的习惯是windows上开发测试,linux下部署发布。当然了,也有的公司直接在windows下部署发布。
2、数据库支持。
网游对于数据库的利用率其实并不高。很多公司项目,只是把数据库当成了一个块存储模块,比如数据库表只有2列,一列是ID主键,一列就是block数据。
这样的数据库存储方式,感觉和文件没什么区别。对开发人员的要求也降低了很多。你可以采用mysql或者mangodb,反正怎么方便怎么用。即不需要编写存储过程,也不需要什么触发器、事务等数据库技术。
3、脚本支持。
游戏的逻辑是整个服务器中最麻烦的地方。策划对需求的改动频繁,程序在性能优化上也重点照顾这一部分。那么这迫切需要逻辑的开发效率高。脚本无疑是最好的选择。目前较为流行的有js、lua、python。js一般是和nodejs配合使用。lua一般需要C/C++程序来加载启动。python则相比起来更完善,可以从C/C++启动,也可以整个服务器完全用python实现。python是出了名的执行效率低,开发速度快。他拥有极为丰富的第三方库,语法简单,高效的开发效率,游戏开发的不二选择。
现在,一个服务器所需要的技术,已经有个大致了解了。那我们又该如何规划这个服务器的架构呢?

网游服务器,至少需要一个服务器吧,作为服务端,加上网络层、数据库、业务层,采用单进程单线程的方式启动运行。可惜的是,这样的服务器,需要处理的事情太多,他既要负责网络IO处理,也要存储数据和计算逻辑。一般来说,我们会将这样的服务器拆分成3个部分:网关服务器、游戏服务器、数据库服务器。
如下图:
从零开始编写网游服务器-第一篇:服务器设计_第1张图片
1、网关服务器:
主要负责管理客户端连接、处理客户端的登录登出请求,对于其他请求则直接发送给网游服务器来解决。这样的网关服务器,其消耗将集中在网络IO上。我们总是担心网游服务器对于网络IO的负载,但是大家都忘记了这样一个设备:路由器。很多地方,包括城域网,一个路由器所能负载的链接是惊人的。路由器不需要多么高级的CPU,而网关服务器,相当于一个建立在应用层上的路由器,如果给网关配置强大的网络模块,你还需要担心网络IO吗?
另外,网关服务器还起到分割内外网的功能,客户端将只能链接到网关服务器,对于其他内部服务器则是透明的。这也对服务器起到有效的保护作用。
那么,网关服务器的主要功能如下:
a)管理客户端链接,方便进行消息的接收和发送
b)接收到客户端的协议后,如果是登陆协议,网关将解析出协议内容,发送登陆请求到通行证服务器进行验证。验证通过后,发送登陆请求到游戏服务器、由游戏服务器来处理游戏相关的逻辑。比如反馈角色列表。对于其他协议,直接转发给游戏服务器即可。
c)当客户端链接断开时,网关通知到游戏服,做登出处理
2、数据库服务器
主要负责对数据库的读写操作。提供一些最为基本的操作接口等。这样的服务器,无论对于哪个游戏,都将是通用的。
3、游戏服务器
主要负责游戏的业务逻辑计算。

这样的结构,简单而实用。有的公司,甚至都没有网关服务器,就只有一个游戏服务器和数据库服务器。那我只能说,这样让游戏服务器得累死,而且扩展性太差。
网关服务器几乎和数据库服务器一样,写好了就一直通用,一劳永逸。可能唯一需要改变的,就是协议类型了。但是如果你的网络库本身就支持多个网络协议,那这也不是问题了。
当然了,这样的结构也不是绝对好的。在游戏的业务达到一定程度后,游戏服务器将仍然是负载的瓶颈。这时候,就可以考虑将游戏服务器中消耗较高的部分抽取出来,独立成为服务器。比如:

服务器的架构设计,并不仅仅是目前这些就能完成的。不同的项目,不同的需求,对服务器的要求是不一样的。那么体现在架构上,也将有很大的区别。
总的来说,寻找到适合项目的,就是最好的。如果某个游戏,在线人数也不多,你还要划分什么场景服、聊天服,这岂不是多此一举?
服务器在设计架构时,往往秉承的原则是:寻找当前架构中的性能瓶颈,独立成单独的服务器进程来提高效率。

接下来,我们又该如何设计一个服务器内部功能?
作为服务器,一般的需求有:
1)作为服务端,接收来自客户端的链接请求
2)作为其他某个服务器的客户端,需要链接到这个服务器进行业务处理
3)需要直接访问数据库,进行数据库操作
无论是上面的网关服务器、数据库服务器、场景服务器、聊天服务器,一个服务器,都将是这些基础服务的组合。
比如:网关服务器 ,需要一个服务端socket监听来自客户端的链接请求,一个客户端socket链接到游戏服务器,提供服务。
甚至有些服务器,会需要链接很多其他服务器,或者监听其他服务器的数据等。
这时候,如果我们把一个socket服务编写成一个组件,通过配置文件来控制服务器中的网络部分,
那么,上面的网关服、游戏服,在我们看来,还有什么区别呢?
比如下面这个服务器配置:注,这是一个json文件
{
    "front01":{
        "frontend":true,
        "path":"front",
        "log":{"path":"log", "file":"front01.txt"},
        "net":{"host":"127.0.0.1", "port":8001, "type":"tcp"},
        "rpc":[
            {"name":"hall", "host":"127.0.0.1", "port":9000}
        ],
        "init":"front.init"
    },

    "master":{
        "name":"hall",
        "path":"hall",
        "log":{"path":"log", "file":"hall.txt"},
        "rpc":[{"name":"hall", "host":"127.0.0.1", "port":9000, "server":true}],
        "db":{"host":"127.0.0.1", "port":3306, "user":"root", "password":"123456", "db":"db_chat"},
        "init":"hall.init"
    }
}
我定义了两个服务器,front01和master。front01是面向客户端的,相当于上面提到的网关,所以有net配置项。
而front01和master则,通过rpc配置项搭建通信通道。master因为有存储需求,于是还有db配置项。
如果master不需要访问数据库了,直接将db配置项去掉即可。

这种配置方法,以及服务器的抽象方法,我是从firefly和pomelo中学到的。
我参考firefly,自己编写了一套python的服务器框架,我可以很方便的使用它编写一个聊天室,或者其他非网游的服务器,当然,也可以编写网游服务器,
在我看了无论服务器的架构有多复杂,在我这,就是改改配置文件,然后编写业务逻辑即可。
可惜的是,目前仅仅支持了tcp,对于websocket还没有支持。
我将在下一篇文章中重点介绍,我是如何在python中编写一个服务器框架的。

你可能感兴趣的:(从零开始编写网游服务器-第一篇:服务器设计)