系列一:游戏服务器的总体框架
/* QQ: 2#4#2#1#0#6#7#6#4 #表示为空 Mail: lin_style#foxmail.com #替换成@ */
核心,我的并行思路
整体拓扑图
代码执行模块层次
核心,我的并行思路
21:31 2009-12-18
昨晚睡觉的时候,又仔细的考虑了下采取的整个框架模型。前提是要充分利用多核和分布。
方法一:把整个游戏看成一个场景,多线程+锁的肆意执行。想都不用想,代价何其巨大和复杂,抛弃
方法二:为了解决这种异步,加入一个任务队列,并且指定一个线程只能执行几个场景。也就是网络收包还是共同的,场景执行分开了。这样的瓶颈是任务队列需要网络线程、场景执行线程的资源竞争;并且还需要维护一个场景和玩家的关系表,同时可能造成空取。即取得的包不是自己所属的场景。
方法三:既然方法二已经把场景执行线程分开,那何不把网络的连接也分开呢?具体方法如下:
先给若干个场景编号指定好逻辑线程,接着再分配一个网络接包线程,把这个看成是一个完整的游戏服务端。在N核的机器上/不同的机器上,你可以启动N个这样的完整程序,分别指定好不同的场景编号,亲缘到各自的CPU数上。这样就成了一个完整的游戏世界。(也就是吧CPU切成单核单核的应用)然后你要维护的就是,当玩家进行场景切换时,如果场景不在当前的服务端里,改怎么进行跳转,这也就是此次要解决的分布式难题
那么登录模块也可以这么做。一个CPU对应着一个bin,该bin包括一个完成端口的线程(为了防止阻塞等意外可以多个),一个逻辑处理线程,然后一个已经连接的套接字队列。那么,你就得为客户端单独配置一个登陆接口的文件,指定好IP和各个端口。当然,这件事可以交给更新服务器去做。
2009-12-24
今天又机会看了下bigworld的引擎结构图。发现主体的架构是差不多的。让我兴奋了一把。但是bigworld的邮局数据转移,让我大开了眼界,毕竟以前都是自个瞎琢磨。
在我的结构里,最麻烦的就是场景切换时不在一个线程里的数据转移。而BW得做法是,在服务端和客户端的中间设置一排中间服务器,专门用来用户数据的海量转发。这样就不涉及到用户数据转移的问题。而且实现起来也确实直观,决定采用。
整体拓扑图
逐个介绍下每个部件的作用:
Login邮局分配:非常关键的路由。它负责邮局服务器的监控,知道邮局的负载量;负责帮助客户端对邮局的寻址工作。客户端与邮局分配服务器采取的是UDP的连接。原因有二:- 这是一个非常小的通讯交互,客户端发个请求,服务端发个回答,整个开销甚至低于一个TCP的建立
- 服务端寻址的时候非常的简单(事实也是如此,按我的方法仅仅只要一个随机和一个邮局服务器台数的循环量)。即时UDP的出错概率,在1秒内也能做到(1秒/包来回时间)的重试次数,更重要的是,UDP在高并发上有着无限的可能。
邮局服务器:进行客户端和BIN之间的消息转发。
- 接受客户端的高并发连接
- 接受BIN的连接
- 主动向邮局分配服务器连接注册自己 ,理论上可以做到邮局服务器动态的增加
Bin1..Bin4:主程序:你只要为他提供好场景编号,就可以像一个独立的游戏程序跑动。尽量将一些最即时的信息(比如打斗)放在这里面运算。
DPC:用来控制非即时性的消息。比如物品的邮寄,聊天消息的转发,数据存储等。
代码执行模块层次
层次上分为网络层,队列层,和逻辑层。以及一个main的启动初始程序。
整个大致的工作流程就是:在接收方面,网络层收到数据后,统一放到队列里,然后由逻辑层解析,有结果返回时直接由逻辑层发送。
这样设计的原因有二:
- 接收的时候,是一个创建各种任务的抽象过程。自然,肯定有即时任务,非即时任务这样的各种任务属性。因此创建一个队列进行同步的转换是非常必要的。
- 发送的时候,我不进行一个队列的中间过渡,而是选择了交给逻辑层直接发送。虽然耦合度高了点,但是简单性和效率高了很多。首先,发送本身就是个即时的过程,没有优先级之分(起码在这个版本里没有);其次,发送的数据没有附加任何的事件属性,完全是个二进制流的级别,再经过一次队列的转换没什么必要。