服务器架构技术一直是技术热点,比如比较流行的游戏服务器,各种数据平台系统等等都离不开服务器的架构设计,服务器架构设计的好坏直接决定了用户对产品使用的体验,在互联网时代,全世界用户或者玩家的距离已经开始变的越来越小,我们做的各种平台设计要能满足全球用户的使用已经成为了现实,数据的共享已经成为当前急需解决的问题。

游戏服务器在这块技术也已经打通,比如COC海盗奇兵,皇室战争游戏,它们的架构设计采用的就是全球同服架构设计,很多开发者对此感觉很难,其实利用现有的开源技术我们同样可以自己搭建一个全球同服的服务器,而且可以满足实时在线玩家几万到几十万人。下面将搭建全球同服服务器涉及到的技术给读者一一介绍:

全球同服第一个摆在我们面前的问题是时间问题,地球上不同的国家,他们的时差是不同的,不同的时差的人要实现全球同服,需要在服务器这块处理好,解决方案是需要在不同的地区布置一组服务器,这些服务器数据最终要统一到一个数据库中,比如中国大陆玩家可以通过香港服务器登陆到美国服务器,这就要求中心服务器的架设要在国外,所有的玩家数据都可以在美国服务器最终实现统一。布置在世界各个地区的服务器选择,我们选用的开源框架HAProxy框架服务器,它的代码是开源的,网址:https://github.com/haproxy/haproxy。先介绍一下HAProxy的功能:

HAProxy通常是使用在Web端的,也就是平台架构中,使用HAProxy、PHPRedisMySQL就能支撑每周10亿请求,而且扩展性非常好。在平台中的架构设计如下图所示:

现将Application Layer层继续细分可以得到如下所示的图:

在上图中,使用了Symfony2,Varnish,Keepalived,Redis。其中Varnish的主要用处是Varnish是一款高性能且开源的反向代理服务器和http加速器,它是一款开源的框架,网上有源代码;Keepalived主要功能是实现真实机器的故障隔离及负载均衡器间的失败切换,Redis主要是用于存储的。在我们的全球同服 服务器架构上Varnish是用不到的,Keepalived可以用于服务器故障切换,如果一台服务器发生故障了,它会快速的切换到另一台服务器上,在这里会用到xinetd服务的作用是检测端口,HAProxy用http协议检测这个端口是否正常。Haproxy服务器之间的切换图示如下:

接下来介绍数据层,我们使用Redis和MySQL存储所有的数据,MySQL更多作为三级缓存层,而Redis则是系统的主要数据存储。选用redis的优点是:

  • 在存储大量数据时不会影响性能,大约2.5亿记录
  • 通常情况下多是基于特定资源的简单GET请求,没有查找及复杂的SELECT操作
  • 在单请求时尽可能多的获得资源以降低延时
以上讲解的技术大部分都是应用在Web的架构设计上,但是由于它的优点,现将Haproxy主要是作为游戏的节点服务器使用,它的布局如下所示:
不同地区的客户端通过这些服务器登陆,比如中国大陆的玩家可以通过香港的Haproxy服务器登陆,最终会通过gate服务器在美国汇总,我们的数据库是放在美国的,我们的中心中心服务器是在国外的,上图中的服务器是作为地区服务器节点使用的。
Haproxy作为一个开源的服务器它是使用C语言编写的,只能在Linux系统运行,感兴趣的读者可以去学习一下,接下来我们要把世界上各个地区登陆的玩家通过gate服务器将它们联系起来,gate服务器选择的是erlang语言实现的gate服务器,注意的是各个gate服务器之间是相通的,只有这样才可以满足全世界玩家在一起。erlang服务器用的也是比较多的,Erlang最大的优点是方便,很多基础功能都已经集成到Erlang语言中。之前用C++写服务器的时候,管理TCP连接很繁琐,需要写一大堆代码来实现。底层的框架需要写很多代码实现,这样既浪费时间,又会有很多BUG。但是用Erlang就方便多了,底层的一切你都不需要考虑,你只需要考虑,服务器的架构以及业务逻辑。从此让你彻底从底层的泥潭中解脱。可以直接使用Erlang语言搭建游戏服务器如下所示:
 
我们这里只是用它作为gate服务器使用,使用Erlang作为gate服务器可以做到负载均衡,Erlang语言非常容易上手的,这样我们的Haproxy服务器可以直接跟Erlang实现的gate服务器直接链接,gate服务器会做负载均衡处理,同时也解决了世界各个地区的接入问题。对Erlang语言不了解的读者可以查阅Erlang的官方网站:http://www.erlang.org
全球同服 服务器的网关也解决了,接下来就是游戏服务器底层和游戏逻辑的实现了,在这里我们选择了Skynet框架,这个框架是由云风开发维护的,实现了一个多线程高并发的在线游戏后台服务框架,提供定时器、并发调度、服务扩展框架、异步消息队列、命名服务等基础能力,支持lua脚本。单服务器支持10K+客户端接入和处理。Skynet框架如下所示:
 

每个在线客户的客户端,在skynet server上都对应有一个socket与其连接。一个socket在skynet内部对应一个lua虚拟机和一个”客户特定消息队列“(per client mq)。当客户特定消息队列中有消息时,该队列就会挂载到全局队列(global message queue)上,供工作线程(worker threads)进行调度处理。

     skynet的服务处理主流程比较简单:一个socket线程轮询所有的socket,收到客户端请求后将请求打包成一个消息,发送到该socket对应的客户特定消息队列中,然后将该消息队列挂到全局队列队尾;N个工作线程从全局队列头部获取client特定的消息队列,从客户特定消息队列中取出一个消息进行处理,处理完后将该消息队列重新挂到全局队列队尾。

感兴趣的读者可以自行查看SkyNet源代码,网址如下:https://github.com/cloudwu/skynet。每个客户处理消息时,都是按照消息到达的顺序进行处理。同一时刻,一个客户的消息只会被一个工作线程调度,因此客户处理逻辑无需考虑多线程并发,基本不需要加锁。这些消息是从Haproxy服务器发出然后经过Erlang Gate服务器把消息分发给Skynet处理,下面再看看Skynet的并发调度任务方式:

 

上图中,lua支持non-preemptive的coroutine,一个lua虚拟机中可以支持海量并发的协作任务,coroutine主要的问题是不支持多核,无法充分利用当前服务器普遍提供的多核能力。所以目前有很多项目为lua添加OS thread支持,比如Lua LanesLuaProc等,这些项目都要解决的一个问题就是并发任务的组成以及调度问题。并发任务可以使用coroutine表示:每个OS线程上创建一个lua虚拟机(lua_State),虚拟机上可以创建海量的coroutine。

Sky net架构实现结束后开始数据存储设计了,在这里使用了mysql和redis存储,它们的存储方式在前面已经介绍过。Redis 是支持多key-value数据库(表)的,并用 RedisDb 来表示一个key-value数据库(表),不清楚的读者可以去网上百度一下。

整个全球同服 服务器架构的简易设计如下图所示:

以上实现了全球同服 服务器技术架构,可以实现全球玩家同服,细节方面就不多说了,比如使用哪种通信协议,开发逻辑使用lua开发等等。其实,它的主要原理还是利用了Web架构的设计,要假设全球同服 服务器,读者主要掌握Haproxy服务器的学习,Erlang语言的学习,SkyNet服务器架构学习和Lua脚本的编写,以及利用Redis,MySql实现数据的存储。在这里也是提醒读者市面上很多开源的东西如果利用的好也可以帮助我们实现很多事情。国外能做的事情,我们也可以做到。