游戏服务器的网络层架构异变实现服务器多线程

 老手拍砖。转载请注明  ziyebuboka    http://www.cppblog.com/ziyebuboka/
        总所周知,要想服务器的高效率实现,那么多线程是不可避免的,但是游戏服务器的多玩家交互的特殊性又使得在逻辑层实现多线程成为了安全瓶颈,以往的架构也是只有通过结构功能的不同来实现多线程,比如 网络层一个线程 逻辑层一个线程 等等,但是如此并不能有效的通过多线程方式来较好的实现服务器的高效处理。本文就是探讨的如何通过结合游戏服务器的特殊性来异变当前主流的网络包架构模式从而实现服务端的多线程。
      先来看下主流网络包的架构模式:
              网络层单独线程来对LinkManager进行管理,收发消息加入到LinkMessageBuff(单个Link的消息缓存)中---》收到消息加入服务器消息队列中(这里一般可以通过一个消息回调函数将消息BUFF引出加入到服务器的消息队列中),而后是服务器的消息处理线程对消息队列进行处理,拿出消息做逻辑处理。
              如此架构,在通用模式下较好,较好的实现了模块化方式,单独库的形式,并且与服务器的具体逻辑处理耦合度较低(只有服务器开启的一次网络包导出消息的回调函数的注册),但是如此出现的情况是,消息加入到了服务器的主消息队列,而后只有一个消息逻辑处理线程对其进行操作,进行各个逻辑处理,在某种程度上无法较好的实现多线程。http://www.cppblog.com/ziyebuboka/
              这种情况下,我也做过讨论和尝试,一般的处理情况是在主消息队列逻辑处理线程之下再分线程(比如可直接转发线程,或者根据逻辑模块转发线程处理),但是得到的结果是,
               一个是安全上无法做到保证,比如说从主消息队列再直接转发分配到多个线程进行处理,但是这里就无法保证消息处理的先后顺序了,大家都很清楚,在一些玩家的逻辑操作上,先后顺序是需要严格的保证的。
              一个是开发成本过高,比如实现单个逻辑模块单独线程(比如帮派 国家 队伍的消息处理单独),如此的话,在某种逻辑操作上,先后顺序可得到保证,但是带来的后果是开发成本与学习成本过高,在开发过程中将会陷入锁的海洋。。。。而对于一般程序员来说,实在要求过高。。。而且也过于接近下层结构,无法有效隔离层次,实现较好的模块化开发。。
      再来看看异变的架构:http://www.cppblog.com/ziyebuboka/
               首先我们先来说下服务器结构,服务器中的场景(地图)概念很重要,每一个玩家处在服务器中,最真实的就是处在服务器的某一个场景某一点上,每一个场景都必然具备一个PlayerManager来对玩家进行管理。而每一个玩家对应的正是一个Link。
               如果我们反向思维一下。略微的将LinkManager上移一个层次,将其作为一个场景中的单位,其实一些问题就好解决了。下面具体的说下
              我们把网络层对LinkManager进行管理收发消息的线程做多线程处理,但是此处有一个难点,就是你无法知道与控制与为什么是这个线程管理这个Link 而另一个线程管理另几个Link。
              我们将此线程处理也上移一个层次,与场景结合,一个场景一个线程,来管理处于此场景中的玩家Link,每个Link在自己所处于的场景线程中将对消息进行逻辑处理,如此则将前面的几个问题都解决了。安全上可保证,比如,因为处于一个场景中的,则同步信息可保证安全,而对于上层逻辑也无需关注很多的线程概念。而对于不同场景间的交互信息(比如远距离组队,聊天消息),则可通过场景的转发消息加入目标场景消息队列的机制来处理,因而消息处理的安全任然可以保证。
             得到的结果就是以场景为单位来做多线程对网络层进行管理,如此虽然在库的耦合度上有所增加,但是很多问题都可解决了。此为专门增对游戏服的网络包的特殊架构,而摒弃了过于通用性所带来的弊端    
    下面具体的说下网络层上是如何实现:
                 每一个Link都具备了一个socket,玩家加入到一个场景,则将此Link加入到此场景的LinkMange,并将本socket加入到本场景的fd_set中,而后本场景内只需要对存在于本场景fd_set中的socket做select处理,对本场景内的Link做收发消息处理即可。(本场景LinkManager初始化时已加入服务器socket)http://www.cppblog.com/ziyebuboka/
   

//补充1:
缺点的确有,从架构上来说,层次互饶的确不好,但是具备了一定的好处,而且可以从设计模式的角度做好层次互分,我是这么认为的
1:首先 如果还是在网络层一个线程对所有SOCKET做处理 再转发至各个场景消息队列 由各个场景的消息处理线程对其做处理,也的确可实现消息处理的多线程,但是不可避免的,在SOCKET对LINK的管理上任然会出现性能瓶颈,因为在进出口上只有一条路(如果进出口这里直接用多线程,则会出现无法知道与控制与为什么是这个线程管理这个Link 而另一个线程管理另几个Link)
2:如果将LINK的管理层次上到场景级别,缺点是一定层度上与架构第二层次互饶,但是优点是对各个LINK的管理也不再是一条路了,由各个场景LINK管理线程对各个场景内的LINK做管理,也是多条路了,而后再发至各个场景消息队列,又各个场景消息处理线程对消息做逻辑层次处理,可解决瓶颈
3:从模式角度来解决耦合度的问题,制造SceneLinkManager 挂接各个场景,制造各个SceneLinkManager线程,服务器启动根据场景来生成,而SceneLogic与PlayerLogic仍在上一层次,消息进入队列从Link找到Player,并且再制造每个场景的消息逻辑处理线程,消息队列的逻辑处理只在这里面进行,因而我们仍然认为此Manager还是属于网络层次,对于上层逻辑无需关注SceneLinkManager的操作。  

//补充2:
@expter
你说的这个就是文里面一开始提到的主流线程的架构模式了。网络层一个,逻辑层一个。安全性等等肯定都是可以做到非常好的,但是效率上肯定就差了,其实各个场景本来就是独立,完全可以单独开线程,这个文讨论的就是如何用安全高效的方式来解决多线程
10:27 不知道 在回复中所提到的也是一种主流方式,在消息的逻辑处理模块为各个场景单独开线程,但是网络层收发消息的处理上仍然还是一个,所以我这里提出的就是可以依据游戏服务器的特殊性,以场景为单位来管理本场景内的LINK,在网络收发处为每个场景开线程来收发。
也有一种情况可以是直接在网络层为link分组各个线程管理,比如预生成的1000个Link,0--499号Link是A线程管理,500--999是B线程管理,但是这样会在消息同步等方面出现问题,比如A玩家和B玩家打架 A砍了B一刀,B砍了A一刀,这时候在先后顺序上就有可能出现问题了,A的Link在A线程,B的Link在B线程,有可能是A的操作是先做,但是B线程先Tick了,导致B的砍一刀比A的砍一刀先处理了。。所以我这里提出是以场景为单位来管理Link 则这种问题就会避免了。。
我这里提出的模式就是网络层以场景为单位线程管理本场景内LINK,而后是各个场景再来各个的逻辑处理线程处理各个场景的消息队列        


         结束语,本文写的不好,有不清楚的地方提出我再加。欢迎评论与讨论,也希望能得到更好的方式。 http://www.cppblog.com/ziyebuboka/

posted on 2010-04-25 00:35 ziyebuboka 阅读(2697) 评论(10)  编辑 收藏 引用 所属分类: 游戏服务器

评论

# re: 游戏服务器的网络层架构异变实现服务器多线程 2010-04-25 10:27 不知道

没有必要把底层socket操作提到这么高层次来,缺点很明显。
其实只要有个模块控制玩家在哪个场景就行了,底层socket收到的消息交给他,有他来分发给正确的场景线程处理。  回复  更多评论   

# re: 游戏服务器的网络层架构异变实现服务器多线程 2010-04-25 11:57 ziyebuboka

@不知道
缺点的确有,从架构上来说,层次互饶的确不好,但是具备了一定的好处,而且可以从设计模式的角度做好层次互分,我是这么认为的
1:首先 如果还是在网络层一个线程对所有SOCKET做处理 再转发至各个场景消息队列 由各个场景的消息处理线程对其做处理,也的确可实现消息处理的多线程,但是不可避免的,在SOCKET对LINK的管理上任然会出现性能瓶颈,因为在进出口上只有一条路(如果进出口这里直接用多线程,则会出现无法知道与控制与为什么是这个线程管理这个Link 而另一个线程管理另几个Link)
2:如果将LINK的管理层次上到场景级别,缺点是一定层度上与架构第二层次互饶,但是优点是对各个LINK的管理也不再是一条路了,由各个场景LINK管理线程对各个场景内的LINK做管理,也是多条路了,而后再发至各个场景消息队列,又各个场景消息处理线程对消息做逻辑层次处理,可解决瓶颈
3:从模式角度来解决耦合度的问题,制造SceneLinkManager 挂接各个场景,制造各个SceneLinkManager线程,服务器启动根据场景来生成,而SceneLogic与PlayerLogic仍在上一层次,消息进入队列从Link找到Player,并且再制造每个场景的消息逻辑处理线程,消息队列的逻辑处理只在这里面进行,因而我们仍然认为此Manager还是属于网络层次,对于上层逻辑无需关注SceneLinkManager的操作。   回复  更多评论   

# re: 游戏服务器的网络层架构异变实现服务器多线程 2010-04-25 17:57 keror

@ziyebuboka
网络层要换成iocp,怎么办?
你说的优点都不是优点。  回复  更多评论   

# re: 游戏服务器的网络层架构异变实现服务器多线程 2010-04-25 21:54 ziyebuboka

@keror
通用完全独立模块和效率必然要选择其一了。  回复  更多评论   

# re: 游戏服务器的网络层架构异变实现服务器多线程 2010-04-25 21:55 ziyebuboka

我觉得开发中还是需要在必要的地方结合特项目的特殊情况做特殊考虑与处理,
这也就是一种构思了,还需要一些具体实施
从设计模式角度来说,肯定是不够好的了,要不怎么叫异变呢。。。
不过从性能上来说,个人认为还是提高的。。。  回复  更多评论   

# re: 游戏服务器的网络层架构异变实现服务器多线程 2010-04-25 23:16 expter

我觉得可以分为网络层和逻辑层
网络底层接收发消息,
逻辑层处理消息。

逻辑只有1个主线程,不停的响应所有消息和定时器触发.  回复  更多评论   

# re: 游戏服务器的网络层架构异变实现服务器多线程 2010-04-25 23:37 ziyebuboka

@expter
你说的这个就是文里面一开始提到的主流线程的架构模式了。网络层一个,逻辑层一个。安全性等等肯定都是可以做到非常好的,但是效率上肯定就差了,其实各个场景本来就是独立,完全可以单独开线程,这个文讨论的就是如何用安全高效的方式来解决多线程
10:27 不知道 在回复中所提到的也是一种主流方式,在消息的逻辑处理模块为各个场景单独开线程,但是网络层收发消息的处理上仍然还是一个,所以我这里提出的就是可以依据游戏服务器的特殊性,以场景为单位来管理本场景内的LINK,在网络收发处为每个场景开线程来收发。
也有一种情况可以是直接在网络层为link分组各个线程管理,比如预生成的1000个Link,0--499号Link是A线程管理,500--999是B线程管理,但是这样会在消息同步等方面出现问题,比如A玩家和B玩家打架 A砍了B一刀,B砍了A一刀,这时候在先后顺序上就有可能出现问题了,A的Link在A线程,B的Link在B线程,有可能是A的操作是先做,但是B线程先Tick了,导致B的砍一刀比A的砍一刀先处理了。。所以我这里提出是以场景为单位来管理Link 则这种问题就会避免了。。
我这里提出的模式就是网络层以场景为单位线程管理本场景内LINK,而后是各个场景再来各个的逻辑处理线程处理各个场景的消息队列  回复  更多评论   

# re: 游戏服务器的网络层架构异变实现服务器多线程[未登录] 2010-04-26 08:51 true

逻辑层,按功能划分模块,每个模块由一个待处理队列,模块进一步分为有序模块和无序模块,有序模块由一个线程单独处理,无序模块多线程处理。  回复  更多评论   

# re: 游戏服务器的网络层架构异变实现服务器多线程 2010-04-26 09:38 ziyebuboka

@true
你说的这种模式也是使用较广的,而且比较适合使用在账号代理角色代理上,比如玩家的定时保存信息,完全是无序不需要保证先后顺序的,则可以在逻辑层直接转空闲线程处理,而玩家退出登录这类要保证先后顺序的操作就必须要由一个线程单独处理。在逻辑模块的开发上我个人还没成功使用过,有序和无序的分开解决,是解决了安全问题,但是无形中进一步加大了开发上手度和难度,而且各逻辑模块间也再也不能直接交互了,只能以消息棒的形式交互了。  回复  更多评论   

# re: 游戏服务器的网络层架构异变实现服务器多线程 2013-09-02 00:48 sete

……开篇第一句话就说错了的,怎么办?!?!?!  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

<2010年4月>
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

统计

常用链接

留言簿(2)

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜