游戏服务器架构调研报告

隐身守侯 提交于 2019-12-07 17:33:59

服务器架构调研报告

                                                                                                                                                     刘源霖20151119

1.      前言

本文档主要是调研分析新的手游服务端架构,为下一款手游服务端研发提供可参考的方案。主要的参考点是数据持久化,并发效率,分布式,沙盒机制,热更新机制,研发维护成本。如果从头根据需求开发一款新的服务器架构,需要大量的时间,而且可能会得不偿失,一款新的架构稳定都需要时间的。我们应该尝试使用开源的,成熟的,活跃度高的开源框架。正文将先从服务器设计需要考虑的因素进行需求分析,以至明确我们需要的服务器架构原型,然后再分析现有的开源架构方案。

 

 

2.      游戏服务器设计因素

2.1.             服务器运行平台选择

服务器操作系统大多采用UnixLinux操作系统,而Linux发行版本系统中,多使用CentOSRedhatUbuntuGentooDebian。而这些发行版本可以大体分为两类,一类是商业公司维护的发行版本,一类是社区组织维护的发行版本,前者以著名的RedhatRHEL)为代表,后者以Debian为代表。Redhat的稳定性和硬件兼容性都比Debian高。并且RHEL的生命周期是710年,基本上可以覆盖硬件的生命周期,也就意味着一个新硬件安装以后,不用再次安装操作系统。而Debian的生命周期是不固定的,一般新版本发布以后,上个版本再维护18个月。而Debian的版本发布时间间隔不稳定,经常会延期。综合起来一个版本的生命周期一般在34年。如果选用了 Debian 或者 Ubuntu作为服务器,等生命周期过了以后,就没有安全补丁,服务器就会有安全风险。

 

基于以上对比,在给服务器选择Linux操作系统时,我们会优先考虑Redhat系统的操作系统。

由于CentOS源于 Red Hat 企业级 LinuxRHEL)的源代码,依照开放源代码规定释出的源代码所编译而成。由于CentOS开源特性,选择CentOS可以降低成本,同时又能够享受RHEL的服务支持。目前市场最大的两个centos系统是centos52007)和centos62011),最新的是centos72014),每一次的大版本升级系统意味着更好的稳定性和功能扩展。由于centos7刚刚在2014.7月发布,最新版本7.2稳定性还有待考证。所以选择centos6是最为合适的。 Centos6版本最新centos6.7。

 

2.2.             数据持久化

对于服务器而言,数据是最重要的。有一个好的数据持久化方案,对于服务器开发将会是事半功倍。在介绍数据的持久化方案以前,我们现在介绍服务器数据读取和修改的运用场景。服务器运行过程中会不断的生产数据(增删读改),并且将数据落地(如存入数据或者文件)。

但是如果服务器每次都直接从数据库或者文件读取和修改数据,那么服务器的io操作可能成为瓶颈,可以采用异步读写方案来降低io读写对服务器逻辑处理能力影响,但是同时提高逻辑的复杂度。 所以较好的方式是将数据缓存到内存中,同步读写,每次读写都先操作缓存,然后再由缓存同步到数据库。同步机制决定了数据持久化能力。如果自己去实现这套机制无疑是造轮子,而且效果不一定好。数据持久是每个服务器都需要的。已经有很多专业的团队提供有很多开源的成熟的解决方案。如memchache redis 缓存系统。

下面提供三种方案:

第一:redis + mysql

第二:redis + unsqlite(推荐,两种都是key-valuenosql

第三:redis.redis 自带数据落地策略)

 

对于具体现在那种可以后续再讨论

注意:对于在选择数据持久化方案时,需要考虑我们的对数据的需求,如是多读少写,还是少读多写。在游戏服务器中还应该考虑合服和跨服等需求是否方便实现。

 

2.3.             服务器并发处理能力

对于游戏服务器而言,首先应该保证的就是高并发处理能力,从而最大限度的提高服务器的吞吐率,提高单服在线人数,降低服务器硬件成本。实现的方式有很多,没有决对好坏。只有针对具体的需求,才有好差之分。

方式有很多,常用的两种:单线程多进程,单进程多线程

第一:主逻辑单线程+[多辅助线程]

好处:不用考虑锁,多线程数据同步等。开发门槛低,开发方式方式灵活,bug定位容易,新人上手容易

坏处:无法充分利用cpu多核

对于游戏服务器架构而言,服务中存在很多模块,所以的模块都将混入这个单线程中,如果cpu性能出现瓶颈, 优化成本将会非常高,由于是单线程,抛弃了锁等束缚,给程序员提供太大的空间,往往在书写的过程中比较随意,如果没有沙盒机制,要保证代码的质量对参与开发的每个程序员的水平要求都要要比较高。

第二:主逻辑多线程+[多辅助线程]

好处:充分利用多核

坏处:开发过程要考虑数据同步等问题。Bug定位困难

由于单核cpu的能力限,并且游戏服务器模块较多,并非处理某一类单一问题,所以服务器的实现应该尽量利用cpu的多核处理能力,而且可以绑定线程,提高cache命中概率。但是多线程并发编程如果没有一个好的系统机制或者语言支持,如使用c 或者 c++语言。门槛较高,要求开发人员的必须具有较高的架构能力,否则将会出现多线程处理能力不如单线程。所以如何降低并发编程的门槛也是服务器架构设计的重要部分。

 

 

2.4.             服务器分布式能力

服务器分布式,是指服务器支持分别部署在不同物理机上。可以以增加物理机的方式提高服务器的承载能力。所以好的服务器应当有一个灵活的扩展分布方案。所以如何让我们的服务器具有灵活的扩展能力,将是服务器架构中的重要部分。

 

 

2.5.             服务器沙盒机制

沙盒机制:就是让代码在一个比较安全的受包含的环境中运行,即使代码出现错误,产生了叫恶劣的影响,其破坏能力将被禁止在沙盒中不会影响到沙盒外的环境。这样的机制可以提高服务器的安全性和稳定性,并且提高开发效率。所以如何让服务器架构具备沙盒能力将是服务器架构设计的重要部分。

 

2.6.             服务器热更新机制

如果服务器能够做到不停服维护,那么可以在不承受停服带来的损失的情况下,修复服务器缺陷。然而我们通常应的c语言,c++ java的语言都是不支持热更新。通常只有解释型的脚本语言才能支持人更新,还有函数编程语言也支持简单热更新,因为函数编程语言函数内无状态。

 

 

2.7.             服务器研发维护成本

相信有很多满足上面要求的服务器架构,但是选择这些架构需要根据团队成员的自身能力。比如erlang语言,天生支持分布式,支持沙盒机制,由于本身是函数式编程语言,天生时候并发编程,简单热更新等机制。但是团队成员中没有一个会erlang,我们也不会选择erlang语言。因为我们耗不起从头学习erlang的时间。

 

 

3.      调研报告

调研报告主要从两个方面进行:语言、开源引擎。

3.1.             语言调研

现在有很多面向并发或者易于并发编程的语言,他们大多是一些小众语言,如goerlang语言。很多游戏喜欢用erlang语言做服务端。Erlang是函数式编程语言,接近自然语言易于理解,并且天生支持并发编程,热更新。Erlang的核心概念是节点,一个节点就是一个独立的系统包含了地址空间和独立的进程集的完整虚拟机,这种设计基础让erlang语言天生具有分布式和沙盒机制。根据erlang的这些特性,无疑完全满足我们对服务器的要求,而且大大降低了门槛了,因为我们不在需要考虑并发,考虑分布式,考虑锁等。但是一般小众的诞生都是为了特地的解决某一类问题而诞生的。自然就有这种语言最适合的,和不适合做的事情。Erlang适合做非计算密集超大并发服务器,不适合做数值计算和业务逻辑非常复杂的系统。

 

通过上面的分析,erlang语言确实适合轻量级游戏的开发,而且开发门槛低,特别是对于没有经验和技术沉淀的小公司而言。但是我们团队成员都没有学过erlang语言,都没有学习过任何函数式编程语言,虽然erlang的门槛低,但是要用好,相信还是需要的时间的。这将会大大提高的我们的时间成本。所以对于我们选择erlang是不太合适的。但是erlang的思想是可以借鉴的。如果我们能用熟悉的语言实现erlang的思想,无疑是非常完美。那有没有一套这样现成的系统,答案是肯定的。 Skynet(开源服务器引擎)正是研发者在使用erlang时出现瓶颈后使用c语言和lua语言模拟了erlang的机制,最终替换了erlang语言。并且经过日活跃用户50万的考验。具体细节后面具体章节再讨论。

 

3.2.             开源引擎调研

能够站在巨人的肩膀上,利用前人的研究成果,是最快最有效的方式。开源的游戏服务器有很多,根据我们自身的因素,我们只能选择c/c++ lua相关的。这样的服务器引擎目前比较活跃的有两个:KBEngine(c++ + python),  skynet(c + lua)

KBEngine:

发布时间20126月,

开源地址:https://github.com/kbengine/kbengine

Fork次数766

Issues  22

Pull requests 83

Star:891

最新release版本:V0.6.21

社区关注人数:1773

成功产品:

 

KBEngine是一款开源mmog服务端引擎, 使用统一协议能够轻松与前端对接,能轻松使用unity3d ogre cocos2d html5等作为前端表现。

底层框架由c++编写, 逻辑层使用python(支持热更新), 开发者无需重复实现一些通用的底层服务端技术, 使开发者能够真正集中精力到游戏开发上来, 快速打造各种游戏。

KBEngine底层架构被设计为多进程分布式动态负载均衡方案, 理论上只需要不断扩展硬件就能够不断增加承载上限,单台机器的承载上限取决于游戏逻辑本身的复杂度。


Skynet

发布时间:20127

开源地址:https://github.com/cloudwu/skynet

Fork次数:1469

Issues16

Pull requests: 192

Pull requests: 192

Star:3038star> 3000, all1386 c 46 c++ 28

最新release版本:V1.0.0

社区关注人数:1213

代表产品:陌陌争霸(日活跃50万,大服结构),天天来战, , 战神黎明,逍遥西游

http://tech.qq.com/a/20140325/020893.htm

 

skynet是云风编写的服务端底层管理框架,底层由C编写,配套lua作为脚本使用,可换python等其他脚本语言。

skynet默认开启_timer线程、_socket线程、_monitor线程以及可配置个数的多个_worker线程,启动skynet服务就是向skynet注册由c编写的so模块实例也就是服务。

skynet主要工作是通过模块注册管理服务,并协调服务之间的调用和通讯。

 

两者数据对比分析,发布时间相同,社区关注度kbengine更胜一筹,但是参与开源贡献和维护方面skynet更胜一筹,并且skynet已发布1.0版本。 Kbengine目前没有成功的产品。为什么会出现这种情况呢, 第一:kbengine 封装完整,代码量大,用户可以很简单快速的搭建游戏服务器,但是很难把握其底层整个架构,一旦出现问题很难跟踪,这就是为什么参与其开源贡献的人比较少, 而skynet至提高一个简单框架,代码量少,使用者很容易把握住,虽然没有kbengine那么方便,但是也提供更多自由选择的空间。第二:skynet成功的产品都主导研发skynet的简约公司的产品,而kbengine没有成功的产品,有可能是有产品用了没有宣传,还有可能是研发者并没有将引擎用于自己的项目,只是业余时间开发。

 

总之从两者来看,skynet更适合,skynet只提供简单框架,完全模拟erlang的思想,并且采用了我们熟悉的两者语言luac, 并且代码量少,容易把握。并且已有成功的产品和相对客观数据验证了skynet的健壮性。下面对skynet架构做具体分析。

 

 

4.      Skynet 架构解析

在正式具体的介绍skynet设计细节之前,先来分析它是如何模拟实现erlang思想的。

Erlang的主要几个核心点:节点核心概念,分布式, 并发编程,actor 模式,沙盒机制, 多进程实,热更新,开发简单。

Skynet核心概念也是节点,一般称之为服务,也使用actor模式,也提供分布式方案(harbor cluster模式),多线程驱动, 与lua结合,利用lua天生的沙盒机制(state)和热更新机制,同时通过lua来降低开发门槛。简单说:skynet 利用了操作系统线程+luaState + lua coroutine实现了简单的erlangErlang底层也是采用c实现的,只是封装的更加完善,没有接种其他的语言实现自己是的沙盒方式。

 

 

4.1.             Skynet简介

skynet是简约公司云风主导研发的轻量级服务端底层管理框架,底层由C编写,配套lua作为脚本使用,可换python等其他脚本语言。整个服务器逻辑层偏lua。底层C框架主要工作是通过模块注册管理服务,并协调服务之间的调用和通讯。

Skynet 发布与20127月,201511月正式发布release版本1.0

简约公司产品都使用了skynet框架:如陌陌争霸,天天来战,陌陌弹珠,逍遥西游等

设计综述:http://blog.codingnow.com/2012/09/the_design_of_skynet.html

 

 

4.2.             Skynet层次

Skynet是设计是使用actor模式,全部工作线程以消息驱动。服务器初始化启动多个工作线程,一个网络线程,一个定时器线程,一个监控线程。用线程带动各个服务运行,如果是lua服务则需要一个lua state 协助运行,在lua state可以启动多个coroutine协同工作。详细见其层次图。

 

 

 

4.3.             Skynet任务调度

由于skynet使用actor模式,使用消息驱动,那么全局就需要维护消息队列。Skynet的消息队列设计如下,每个服务有个独立消息队列,全局有一个全局的全局队列,每个服务启动后需要将自己的消息队列注册到全局队列中, 所有的工作线程依次轮询全局队列,以此调度各个服务执行自己的任务。

每个消息队列都是多个生产者和多个消费者。见下图

Skynet采用的自旋锁

 

 

4.4.             Skynet-lua 服务

每一个lua服务都有一个独立lua state,由此形成服务器的沙盒机制。配合lua中协同开发模式,可以同时执行多个任务。 对于c层的异步接口,可以在lua层封装一层阻塞接口,然后交给coroutine去等待。等待有结果时再唤醒coroutineCoroutine的阻塞并不会导致整个lua state的阻塞,所以整个工作线程还是在运行的,简单的说就是用同步写异步。如:lua socket

 

4.5.             Skynet-harbor集群模式

当单台机器的处理能力达到极限后,可以考虑通过内置的 master/slave 机制来扩展。

每个 skynet 进程都是一个 slave 节点。但其中一个 slave 节点可以通过配置 standalone 来多启动一个 cmaster 服务,用来协调 salve 组网。对于每个 slave 节点,都内置一个 harbor 服务用于和其它 slave 节点通讯。

 

4.6.             Skynet-cluster集群模式

skynet 提供了更具弹性的集群方案。它可以和 master/slave 共存。也就是说,你可以部署多组 master/slave 网络,然后再用 cluster 将它们联系起来。当然,比较简单的结构是,每个集群中每个节点都配置为单节点模式(将 harbor id 设置为 0)。

要使用它之前,你需要编写一个 cluster 配置文件,配置集群内所有节点的名字和对应的监听端口。并将这个文件事先部署到所有节点,并写在 Config 中。

4.7.             Skynet部署方案

以下是skynet主导研发云风在腾讯课堂上对skynet提出的两者部署方案。

 

 

5.      Skynet实例

 

5.1.             skynet实例-单服

Skynet实例介绍,过程参考下图:

1.  本实例是将登陆单独出了一个skynet节点,也可以与游戏服以前做成同一个skynet

2.  登陆的主服务负责接收登陆消息,从节点负责验证

3.  网关服务负责接收网络消息,并转发给对应的agent

4.  Redis主服务负责接收数据请求,并派发给从节点,从节点负责从各自连接的redis数据查询更改数据。用于数据库连接池

5.本方案只考虑单服

 

 

5.2.             skynet实例-多服

对于多服的实现并一定时因为单服的承担能力不够,有可能是运营需要,运营需要多个排行榜,以养更多的大R,如很多页游开了几百个服,其实是开了几百个排行榜。所以实现为服务架构支持灵活的多服部署方式和合服策略是很重要的,合服操作的是数据,所以要保证在数据标识设计时要为后期的合服做准备。下图是skynet多服部署图,采用使用skynet cluster模式。

 

 

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!