« 目前我们的游戏服务器逻辑层设计草案 | 返回首页 | lua cclosure 的 upvalue 数量限制 »

心跳服务器

我们目前游戏服务器的初步架构是一个连接服务器处理来自多个客户端的连接数据,这个服务器将所有数据汇总后通过一个 socket 发送到后方的逻辑服务器。这个设计曾经写过一篇 blog 提到过

今天,我在两个服务器之间加入了一个控制心跳的服务器。其原始作用很简单,就是按心跳(目前的设定是 10Hz)从连接服务器上拿到数据,再转发给逻辑服务器。并把逻辑服务器发出的数据转出。

为什么这样做?首先,逻辑服务器不再依赖系统时间。它周期性的从心跳服务器获得数据,获得一次,就认为时间流逝了 0.1 秒。这样,时间就改由心跳服务器控制了。

其次,在心跳服务器上加了一小段代码。可以把从连接服务器上获取的所有需要转发到逻辑服务器上的数据都记录在硬盘的一个文件上。那么,逻辑服务器就随时可以重新请求心跳服务器重复发送上一次的数据流。

显然,这大大方便了调试。因为,逻辑服务器的内部状态改变仅仅只依赖于从心跳服务器获得的输入。它自己的时钟也是由心跳服务器输出的数据的节奏控制,而不被自己的时钟影响。所以逻辑服务器上的伪 timer 也只受输入数据的影响。

这样,即使我们实际让服务器工作几个小时甚至几十小时,在心跳服务器回放数据流时,逻辑服务器可以以无等待的过程全速运行,也可以得到完全相同的结果。当然,如果我们暂停下来单步跟踪,同样不会有丝毫影响。

当逻辑服务器由于某个 bug crash 掉时,就可以反复的重现这个 bug ,直到找到问题了。

心跳服务器的代码非常简洁,区区几百行。其任务也非常简单,所以保证其 7*24 小时稳定运行以及高效率运作是非常容易的事情。至于 client 到达逻辑服务器的路程上又多了一台机器引起的效率问题,我觉得大可不必在意。反正在实际运营情况下,玩家的机器连到服务器的机房,已经路过很多机器了,不在乎加这么一台 :)

Comments

13楼说的是对的,玩家途径的设备一般就工作在TCP/IP的2,3层(主要是路由器居多,偶尔有一些防火墙之类的跑到TCP/IP的4层),而这里的心跳服务器很显然工作在TCP/IP的4层(普通计算机TCP/IP的3和4层有一个质的区别,一个在内核,一个在用户空间)。

不过我觉得这唯一的(新增的)一个TCP/IP 4层设备并不会造成瓶颈,如果连这个地方都要优化出来,那这个游戏要不要上线才是最应该考虑的问题了。

另外,我在想,能不能直接让逻辑服务器去gateway取数据,心跳服务器只是起一个kick的作用,这样就避免多一次的数据拷贝,又可以控制回放数据(gateway存储数据)。

虽然已是6年前的文章了,仍然给我很大的启示,谢谢云风大侠,每次来你的博客,总是获益非浅.

作为一个新人,我不好说太多,但我觉得首先云风在这里与我们分享他的设计就已经很好了,我做为一个只接触了网游开发2年去评论游戏服务器架构显然还够资格,但因公司需要,在开发服务器端又没什么经验,开发了两个平台全部是自己琢磨着弄点,有时候真的很想看下传说中的大牛怎么设计游戏服务器,这两年开发的服务器进行了几次大的改动,但总觉得还是不够专业,不够强大,但我在网上寻遍了,只有零星的一点关于网络服务器的简单通信的资料,所有的所谓有经验的,很牛的人基本都是点到为止,基本没有人愿意共享知识,虽然很多东西都靠自己去琢磨,去研究,但有些东西还是需要经验,外网和内网区别很大,大公司那些几十万或几百万同时在线这种体验对于一些小公司的开发人员来说永远都只存在于脑海中的理论。我觉得服务器端除了出色的硬件以外,服务器的架构设计就是最核心的东西了,但我们一些所谓的大牛在钱的面前就是一个狗了(虽然有点过了),没有人愿意分享自己的设计,一但有了一个好的设计,第一个想到的就是赶紧找投资,成立公司赚钱。我想如果Linus Torvalds 身在中国,可能永远不会有现在的linux了,我只想说的当别人拿出一个想法出来分享的时候,我们不应该总去找别人想法的缺点在哪,别人的想法哪点没有我的想法好,然后去大肆抨击别人的想法怎样的无知,怎样的幼稚,这样除了你可能得到一些心理上的过瘾别无其他。
我只是一个简单的程序员,胡乱的表达了一下这么多年的感受。

Can't seem to understand the benifit of such a design...Heart-beating signal might be useful to synchronize the cluster --- or even better --- behaveing like a cluster manager...but in a busy cluster serving millions of users, this kind of design would lead you into a miserable failure...

我们现在逻辑服务器只有两个输入,一个是连接服务器的数据输入,一个是数据服务器的数据输入。

现在两者都是可以被录下来的,数据服务器的数据估计就是你说的上下文了。

数据记录的问题是这样的,一般在测试期间开启,按我们的内部测试惯例,服务器一天维护一次,只需要保证 24 小时的运行数据不至于太大就可以了。100G 的硬盘应付下来问题不大。这个是调试使用的,写一个简单的工具就可以从里面提取许多有用的信息,也可以在 bug 实在找不到的时候做回放,让逻辑服务器在调试状态运行。

只是记录了连接服务器的数据,逻辑服务器当时的上下文没有,也无法重现当时的运行情况吧?怎么调试?
不解,请教!

出错多的时候在于测试期,这个时候服务器压力不大,别说 10x, 100x 都做的到。

现在的网游很少需要 100 天连续工作,常规是 7 天一维护。

我们单独设计了数据服务器,所有数据都是通过那里的。开始录制时的服务器本身的运行状态也可以从数据服务器恢复。

数据服务器上的数据流量远小于通讯数据流。

通讯数据录象想法不错,但是好象要完全还原整个过程也是不可能的。数据通讯都是有前后因果关系的,开始录制时的服务器本身的运行状态也是未保存的,除非从服务器开机起一直录制下去才能真正重现流程。那么如果真的在100天上出了问题,那么势必要从0day开始回放执行才能真正抓出bug,即使以10x的速度执行,也要不短的时间才能到达错误点那。。

"这个地铁在客流量很大的时候会成为瓶颈的,而且也是single point of failure"

这个分析过原因吗?我们还没有做压力测试,想听一下。

好玩,本来像taxi,数据来了就直奔目的地,现在像地铁,必须经过站台走,每过一定时间间隔来一班把积攒的payload都带走~~ 偶们试验过,这个地铁在客流量很大的时候会成为瓶颈的,而且也是single point of failure。不过对于低于100Mbps的流量是没关系的。

流水线能提高效率,现在比较流行的服务器端模式就是分阶段服务器,也就是多流水线方式。我认为多流水线方式的有点云风已经说过了,缺点是复杂度上升,可能会带来下面几个问题1,可能各个流水线都要访问一些全局信息,这些访问就需要同步,如果访问过于频繁基本上多流水线就失去了意义。
2,如果经过的流水线太多或者某个流水线消耗时间太长,可能会导致后续流水线可能做大量无用的计算,因为在这个期间,该用户已经下线。

不过最近新闻老是看到AMD和Intel不停的推出双核,四核的CPU :D

我觉得队列的好处也在于提高效率。正如在一段时间里, CPU 的设计也偏向于越来越长的流水线一样,虽然流水线长了,会增加单个事务的处理时间;但是因为每个环节的处理能力增加了,结果提高了整体的吞吐量。

这段时间看到云风大哥blog上服务器切分的设计,包括之前网络层服务器的和现在的心跳服务器,我理解这种设计的关键在于两点,第一是分层、第二是队列。
分层是为了让每一层只做一件事情,简化逻辑,队列是为了避免复杂的线程同步。除此之外,物理的隔绝或者进程的隔绝我把它理解成一种可选项,仅仅只是部署的问题。
我想,若是在单个进程中,用一个进程检查队列处理逻辑,其余线程连接网络将命令压入,也是一回事吧。
请问云风大哥可以这样理解吗?

对服务器逻辑的优化可以有一个衡量指标了
===> 这一点我深表同意,服务端的优化很不好做,主要是瓶颈难找,并且不好衡量。真实的压力环境保存意义重大:)

对网络数据录象还有一个额外的用途,可以通过反复重现同样的过程,做性能测试。对服务器逻辑的优化可以有一个衡量指标了 :)

如果连接服务器和心跳服务器同一台机器的话,连接服务器和心跳服务器可以通过共享内存来交换数据,不通过TCP/IP,可以稍微提高一点性能。

我们目前的方法是把连接服务器和心跳服务器放在同一台物理机器上,接两块网卡,每个服务各用一块。两个进程采用进程间通讯。

这两个服务业务部分很简单,基本不需要维护,他们也是同时启动和关闭的。(如果需要,可以被维护者看成同一个服务)

这个设计以后也可能被扩展,因为玩家连入的数据往往可以被分到不同的地方处理,比如拆出聊天的部分和位置校验的部分以及设计广播的协议。这个时候需要做运算量更重的包解析工作。

而连接服务器之前还有用户登陆认证排队这些事务,把这些东西拆分到不同服务进程中可以方便的根据机器硬件条件组合。

首先要说的是下面的朋友请针对问题不要针对人。
多加服务器对于出口带宽足够,用户和用户之间的业务逻辑无关联的应用是可以提高性能(后台数据库也可以做到集群的情况下)比如HTTP。
对于游戏服务器增加服务器用处并不大,除非增加新地图服务器,人为的把一部分用户和用户的逻辑移到新的机器上。
做完美的解决方案要结合实际。一般连接服务器的负载很小的,把心跳这样的业务逻辑加上去并无不可,对于运营来说多一台机器多一台管理难度,出问题要多检查一台机器。

以实际我们曾经有过的实际项目而言,一组游戏服务器上峰值时最大吞吐量不超过 10M bytes/s 。一般情况下要小一个数量级。

一秒转发 10M 数据即使用一台 pentium 和一块普通网卡也不会有什么性能或延迟的问题, 如果需要的话,也不用独立的机器运行。

随着游戏逻辑逐渐复杂,游戏服务器的负载在 cpu 而不在网卡上。剥离网络事务的处理,是为了让逻辑服务器专心做好它该做的事情。

数据记录的问题是这样的,一般在测试期间开启,按我们的内部测试惯例,服务器一天维护一次,只需要保证 24 小时的运行数据不至于太大就可以了。100G 的硬盘应付下来问题不大。这个是调试使用的,写一个简单的工具就可以从里面提取许多有用的信息,也可以在 bug 实在找不到的时候做回放,让逻辑服务器在调试状态运行。

实际运营中数据量太大了,不大可能记录下来重放把,如果采用多线程的话,记录下来也未必有用

首先,正是由于实现的不同,才带来了巨大的性能差别,从而导致根本不能将它们相提并论。所以你的linux机器只能做你们办公室或者更大一点的网的网关,而不能做骨干网上的网关。实际上,即使使用现有的超级通用计算机也无法获得骨干网上需要的性能,更不用说商家可以支付得起的服务器,所以必须使用专门的硬件和结构。至于gfw,一定是用专门硬件而不是服务器来实现的。其实,这种实现不同带来性能迥异而导致无法相提并论并不是什么高深的道理,而是一种生活常识。我们不会因为碟机放碟效果好就想当然地认为电脑上软件放碟也一定会和它效果一样好,甚至可以不买碟机;我们也不会因为后驱车改变了一下实现,就想当然地认为前驱车也获得了同样的载荷分配和转向、制动操控性。可见,面对高负载,不能将不同的实现相提并论,也就是一般地说,不能将玩家途经的机器和你的服务器相提并论。那么,游戏服务器会怎么样呢?也就是特殊地说,能不能将玩家途经的机器和你的服务器相提并论呢?也不能。游戏服务器的负载也是很高的,高到二者的性能不能在逻辑上互换,而不是像你办公室的linux网关那样可以和其他网络设备互换。反过来讲,如果像你说的那样,途经网关和单台或多台游戏服务器对效率的影响可以相提并论,则一台或者一组游戏服务器受不了的时候,一台或者多台网关肯定也受不了了,至少分布层网关是绝对受不了了,那么现在就可以宣布,游戏运营商增加游戏服务器一次,互联网运营商就要升级互联网一次,如果所有游戏运营商都大量增加服务器,那我们很快就可以到达第21代互联网了。你可以从正确的方面(如果存在的话)来说明为什么你添加一台服务器不会带来效率问题,但不能从途经网关没带来更多的效率问题推出途经服务器也不会带来效率问题。

其次,你对OSI七层模型、TCP/IP协议、ISO的七层协议以及它们之间的关系无知到如此地步,已经不是可笑的问题了,你令大家笑得太多了,已经笑不出来了,你真的很可怜,本人深表同情。建议你读一些入门的书籍,要系统一点的,看有没有可能使你至少在这个问题上能分得清this和that的区别,看有没有可能使你至少分得清飞机和螺旋桨、喷气的区别。

是否多一台服务器只是部署的问题,如果真的多一台服务器会造成明显的延时,同样的构架移植到同一台服务器或者同一个进程内都不难的。

我觉得到二十一世纪了还去谈什么 ISO 的七层网络标准是很可笑的,这个标准从来就没有被实现过。现在的互联网早就是 tcp/ip 这个事实上的标准的天下了。

引用 Eric S.Raymond 语:

For a few years it looked like ISO’s 7-layer networking standard might compete successfully with TCP/IP. It was promoted
by a European standards committee politically horrified at the thought of adopting any technology birthed in the bowels of
the Pentagon. Alas, their indignation exceeded their technical acuity. The result proved overcomplicated and unhelpful

那只是实现的区别而已,我们办公室的机器要接入 internet 还要过一台 linux 的机器才能出去呢。

我们的 internet 上还有伟大的 gfw 呢。

把玩家途经的机器和你的服务器相提并论是错误的。一个工作在2、3或者4层,一个工作在7层。一个使用ASIC电路专有结构,一个使用通用部件通用结构。

云风这么是设计是对的,我称为按最坏情况设计,这样在最大负载范围以内,用户可以得到一致操作感,不会因为人少了就流畅,人多了就卡。按最坏情况设计的好处是可以最大程度的优化,batch数可以达到最佳化,表现出来的性能上会更加平稳,最大负载会更大一些。

至于是不是要单独分一个服务出来不是这个问题的重点,分不分各有利弊。

我就知道你是为了优美
但设计更多时候是一种折中,
多一台服务器会给技术部带来维护的麻烦。做到文件或是接口级别的 KISS 就足够美了。

并且做到多个进程中,就把交织性分开了吗?将来进程信息同步上的麻烦带来的 bug 是很不好找的。

怎么处理响应延迟是游戏设计的问题。大多数即时战斗的游戏,按小于 100ms 的延迟设计游戏都是允许的。实际上,像魔兽世界这样的游戏,在 ping 300ms 之下,玩家就可以感觉很流畅了。把 300ms 提高到 250ms 并不会有操作感的提升。至于赛车游戏,10Hz 不够用的话,完全可以把频率提高到 20Hz 甚至 50Hz 。这个设计并不会因为这样做而不能工作。(现阶段的 internet 硬件条件,高于 50Hz 的心跳是几乎不可能实现的)一旦心跳服务器满负荷运行,这样情况在处理大量客户端时很容易出现,按不按心跳转发包已经不重要了,因为这台服务器已经按最大性能在工作了。所以,我们不按心跳转发包得到的响应速度提升只在同时连接数比较少时才有效。

而设置心跳的好处是,逻辑服务器不再因为时间这个干扰因素而使每次运行的过程不同,它减少了时间这个输入量,软件 timer 也非常容易实现。而且,取时间的 api 本身也是要进内核的,会少量影响效率。逻辑服务器只剩下阻塞读写 socket 的 api 调用,每个心跳还只用调用一次。对于移植性来说也是不错的。

至于要不要把心跳服务器和连接服务器以及逻辑服务器采用多线程技术做到一个进程中,我个人认为是不必要的。这违反了 KISS 原则,当然我相信很多人,包括我自己,都有能力写出稳定的多线程服务器程序。但是,让每个程序用心的做好一件简单的事情会让我感觉更为美观一些。

你手里有500块钱,并不代表你就可以随便的花掉其中的100块。
如果发消息不按帧走的话(实际上也会有稍许延迟),那算50毫秒好了,这50毫秒对于ping(也就是用户的关键操作响应时间)来说,就是被费掉的(如果不说浪费的话)。

如果有可能的话,为什么不让用户的响应时间更短一些,操作感更好一些呢

1,用户到逻辑服务器的包多经过一台机器延迟就会变大这个是没有疑问的,对于即时性要求很高的是赛车等游戏里这种设计显示是不合适的。对于梦幻西游这样的游戏应该问题不大。
我个人同意下面一些朋友的意见,这个服务器绝对可以整合到连接服务器里。我个人开发过连接服务器,功能里有类似的功能,当时采用多线程(win)方式,到目前运行良好,该程序到目前已经使用接近3年了。
多线程程序我感觉需要前期把可能出现的可能考虑清楚,后期应该严格测试,能通过测试后期出现问题的可能性不大的。

服务器都是流水线作业的,并没有浪费掉时间。只是让 client 过来消息处理的反应时间不可能低于 100ms 而已。实际上,如果游戏设计成要求在 100ms 内得到相应才能工作的话,估计以现在的硬件条件是没可能办到的。或许开发阶段,几个人在线时可以办到;但是我们做的是 MMO ,要面对上千人在线,所以从游戏设计阶段就必须考虑延迟,所有的战斗玩法设定都必须考虑这一点。延迟不光是网络延迟,还有系统处理延迟。设想逻辑服务器正在满负荷工作,让数据是脉冲方式进来还是连续不段的进来并无差别。而脉冲式的接收数据,可以让逻辑服务器摆脱时间的干扰因素,还可以从接收到的包中抛弃一些不必处理的(比如在同一脉冲中得到同一客户端多个位置同步包)。

而日后硬件条件改善后,我们按这个设计也可以轻易的把心跳改成 20Hz , 50Hz , 100Hz 。

ps. 服务器发出的指令不必延迟。

每秒只跑10帧……也就是说消息进出分别会遭受平均50毫秒的延迟,合计为100毫秒(如果忽略这其中的其它延迟的话)。不知道你们的游戏类型,可能回合制的游戏无所谓,但在服务器内部就这样大手笔浪费掉100的ping,我觉得很难接受。我不相信只有付出这么大代价才能做到消息的无差异回放来方便debug

逻辑服务器连续多次的延迟完成任务肯定会造成客户端很卡。这个是无可避免的。

心跳服务器不负责缓冲数据,缓冲数据是由连接服务器来做的。心跳服务器从连接服务器上拿数据是阻塞方式工作,向逻辑服务器发送数据同样是阻塞方式。所以逻辑服务器不把数据取完,当 os 的 buffer 满后,就自动堵在那里了。当逻辑服务器把任务处理完,连接又流畅后,心跳服务器会根据实际时间连发几个空心跳(无数据),让时间赶回来。然后再从连接服务器取到数据,再发向逻辑服务器做下次处理。

"适当考虑一下数据有序化时的排序依据也是需要的吧。" 这句话什么意思? 什么叫"数据有序化时的排序"? 数据从来都是有序的。

如果逻辑服务器在第二个心跳时钟来临时还没有处理完第一批数据,那么心跳服务器必须缓冲第二批数据。如果逻辑服务器连续多次的延迟完成任务,会使客户端表现出很卡的样子吧。
适当考虑一下数据有序化时的排序依据也是需要的吧。

根据 KISS 原则,把这部分功能分开做。

这台服务器日后还要处理通讯加解密的工作。连接服务器的作用是最大限度的处理外部连接;这台服务器的作用是使数据有序化。

按我们旧有游戏的经验,峰值外部连接数据在每秒 8M 左右(3000个连接)。


提高逻辑服务器的处理能力,并非多线程就可以解决问题的。多线程容易带来结构的复杂度问题,稍有不慎就会有反效果。应该谨慎使用。

对心跳服务器来说,与一个几千人在线的连接服务器,0.1秒要缓冲多少数据,为了保证时序,逻辑服务器使用单线程的话,如何保证能高效的处理完这0.1秒的数据量,还是担心效率问题。
但对于调试来说,你的方法的确不错。

但是放入连接服务器做此功能,可以节约一台服务器,岂不是更好:)

Post a comment

非这个主题相关的留言请到:留言本