赞
踩
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
上一篇《网络游戏服务器构架设计(二)》介绍了刀剑Online的连接负载服务器CLS,博友提出质疑“说得不够详细,比如你怎么,场景服务器怎么才算一个场景服务器,场景服务器切换怎么处理不断线后连接另一个场景的,还有很多细节问题没有说到”,本篇就来介绍游戏服务器最为核心的部分:游戏逻辑服务器,同时也回答了这位博友的问题。
PS:本篇的文章结构主要分两个部分,前半部分(2.2节)介绍刀剑Online如何实现游戏逻辑服务器,后半部分(2.3节)为本人结合实际工作对这套服务器构架做出的一些展开解释及补充,主要对设计思想进行分析。精彩在后面哦!
-------------------------------------------我只是条分割线--------------------------------------------
先来回顾一下刀剑Online的总体构架图:
2.2 游戏逻辑服务器
顾名思义,就是和游戏具体逻辑相关的服务器(这应是一个统称)。这块是网游服务器端的核心部分,不同的游戏差别会很大。在刀剑中,游戏逻辑服务器分为两部分:总控服务器和场景服务器。
2.2.1 总控服务器(Master Server,MS)
关于总控服务器的作用,刀剑Online的主程是这么解释的:
总控服务器(以下简称MS)的作用之一是负责玩家在具体游戏内容之外的操作(即.玩家进入场景服务器之前地操作)。如:登录、注销、各种角色操作(创建、删除、选择)等等。
MS和所有地场景服务器都保持连接,这样它就成为各个场景服务器间的枢纽,当需要一些跨场景服务器的操作或者需要访问别的场景服务器数据的时候,指令都先发给MS,然后MS根据需要再转发给相应地场景服务器或者直接发给相应的用户,并进行后续地协调工作。
比如:在场景服务器1上的用户A希望向游戏中的用户B发出一条添加好友的请求,则场景服务器1向MS发送添加好友指令并附带了用户B的名字,MS查找发现有B这样的用户,则直接把指令发给CLS,然后由CLS转发给B用户;如果没有发现B用户则直接通知A未发现B。
又比如:在场景服务器1上的用户A点中了传送点,将要传到场景X,场景服务器1发现X场景并不在自己的管辖范围内,于是发送转移指令给MS,MS查找发现场景X在场景服务器2上,于是先发送用户A的离开指令给场景服务器1,让用户退回到MS上,然后再发送用户A的进入指令给场景服务器2,并说明用户将要进入的场景为X,这样一次跨服务器的场景转移就完成了。[1]
2.2.2 场景服务器(Zone Server,ZS)
关于场景服务器的作用,刀剑Online的主程是这么解释的:
场景服务器(以下简称ZS)就是具体负责游戏场景的服务器。玩家选择人物开始游戏之后就进入了这种服务器(即开始游戏之后CLS把所有玩家的操作指令都转给ZS)。
玩家的各种操作的逻辑都是由ZS完成的,同时,ZS也要负责各个场景以及场景中的NPC和场景中各个物品的逻辑运行。
每款游戏的真正游戏性的核心就是这些ZS。它的具体细节我就不过多的讲述了,各个游戏的具体内容应该都不相同。不过有几个原则是共同的:
一、是要高效。如果ZS对游戏逻辑的处理效率低,会直接影响玩家同时在线的数量,并导致游戏中的玩家感觉很“卡”,这是除了网络延时之外第二个会造成游戏 “卡”的地方。提高效率的方法除了对代码进行优化外,就是要使用高效的脚本系统,直接把脚本转化为程序代码编译到程序中去也不失为一个办法。
二、是要有灾难恢复机制,就是当ZS发生非法操作时(只要不停电)能够恢复出非法操作时各个用户的数据。这个在游戏运营初期服务器尚不稳定的时候非常重要。虽然我们也可以通过加快用户存盘间隔的方式(比如把每10分钟存一次盘改为每1分钟存一次盘),但是这会成倍加重数据库负担,同时也不能避免由于用户刚好在存盘间隔的时候获得了重要的金钱或道具而导致丢失的情况。刀剑采取的方法是在申请用户关键数据对象的时候通过包装的函数从共享内存中申请,这样即便ZS非法操作了,共享内存并不会消失,在重新启动的时候就可以从共享内存中恢复出程序非法时的用户数据。当然恢复的时候也需要对用户数据进行一些校验,以免把已经被破坏的数据存入数据库。[1]
2.3 设计思想分析
游戏逻辑服务器主要负责汇总所有在线的client发来的各种操作、状态等数据包,经过一系列的处理后有选择的广播给需要的client,从而给所有在线的玩家呈现一个“统一”的世界。
优秀的逻辑服务器架构需要优秀的设计思想,而优秀的设计思想又源于对游戏虚拟世界的适度抽象。抽象可以看作一个工程问题,同时也可以看作一个哲学问题,这正是游戏开发的魅力所在。本系列blog将围绕这种抽象一步步展开,结合不同的项目来诠释网络游戏服务器端的设计思想(希望能做到…)。
站在服务器端的角度对游戏虚拟世界进行抽象,首先要弄清楚构造虚拟世界需要些什么?让我们来想象一下吧(以下内容参照了《盗梦空间》-“Inception”),先来看一段视频:
Inception是我非常喜欢的影片,第一次看到这一段的时候,就感觉非常像游戏设计。今天能把它写下来也算没浪费几十块的电影票钱。
影片中饰演the architect(造梦师)的艾伦·佩姬(女),正在接受莱昂纳多(饰演the extractor-盗梦者)的训练。莱昂纳多说道:“Remember you are the dreamer you built this world。”,“I’m the subject my mind populates it”。值得关注的两个名词:world,subject。这就是我们要讨论的主题。那么构造网络游戏的虚拟世界需要些什么呢?其实莱昂纳多已经替我回答了这个问题:“We create and perceive out world simultaneously. You create the world of the dream, We bring the subject into that dream, and they fill it with their subconscious.”。world就相当于游戏里的场景,而subject就是一个个在线的玩家(player)。
游戏世界(world,这里的world泛指游戏世界及地图,见2.3.2)及游戏对象(object,包括player)是构造网络游戏服务器端时,需要关注的两个重点。如何处理好world和object的关系和地位直接影响到服务器端的构架。
2.3.1 游戏对象(object)
先来看游戏里一般会有那些object,以Mangos为例:
图2 mangos游戏对象的class diagram[2]
对于上面的类层次结构图这里不做展开(留到本系列后面的文章中展开),这里引用mangos的游戏对象是想给读者一个直观的印象,游戏中的object在服务器端是个什么摸样。类的层次是对游戏世界中的对象抽象后得到的结果,抽象需要“适度”:如果类的层次过深,维护起来困难,而且后期往往会导致基类过于臃肿、不堪重负;如果类的层次过浅,一些object的共性不能体现,很多代码会重复出现在各个子类里,复用性差。
2.3.2 游戏世界(world)
游戏世界在本文是泛指游戏对象所在的场景,以及附加在场景之上的地图管理和对象管理,这里统称游戏世界管理。
如何进行游戏世界的管理是一个复杂的工程问题,网络游戏常常会把整个游戏世界分为若干张地图,每张地图又会分为若干个区域进行管理。这若干张地图能无缝的过渡就称为无缝大地图模式(类似WOW),如果像刀剑Online那样只能通过传送服务在两张不同地图之间穿梭的,称为有缝地图。而相对来说服务器端会比客户端简单一些,例:如果在客户端看到的场景是下面这个样子:
服务器端根据划分区域的不同可能看到的地图是这个样子:
这里服务器端用到了tile-based的方式来管理地图,这种tile的方式在2d游戏中十分的常见(打格子),而很多3d网游的服务器端为了减少运算量也采用这种tile模式划分区域进行管理。如果对地图管理有兴趣请阅读云风的blog《用四叉树管理散布在平面上的对象》、《碰撞检测》,本文先不做详细的展开。
2.3.3 游戏对象和游戏世界的关系
如何处理游戏对象和游戏世界的关系和地位,是影响服务器端架构的最直接因素。为了体会到这点,下面将以刀剑Online为例,分析玩家对象(player,游戏对象中最为重要的部分)和游戏世界的关系对整个服务器构架设计的影响。
刀剑Online把游戏逻辑服务器分为总控服务器(Master Server,MS)和场景服务器(Zone Server,ZS,本文提到的游戏世界多指ZS),那么在client成功登陆server后,服务器端应该在哪构建player对象呢?在MS上?还是在ZS上?这是个工程问题,同时也是个哲学问题…
如图中标注的(1)~(4)的步骤,展示了这个过程,编程操作起来还是挺麻烦的。
2. 如果把player放在Zone Server上:这是一种比较常见的方式,player和其他游戏对象在场景服务器中构造,隶属于Zone Server。由ZS来掌管player从编程角度讲好处多多:可以直接获得player的信息,对于场景内交互十分便利,场景内的操作不需要多余的数据传输。而在需要跨场景操作时只需用MS中转一下即可;但是这种方式缺点同样是很致命的,player在Zone Server上构造就失去数据放在中心节点的简洁,player的信息常常需要在ZS、MS和DB上进行同步,这种“三体同步”的痛苦只有做过的人才能体会。比如:几个独立的服务器之上需要增加一个跨服服务器,这几个普通服务器的玩家可以进入这个跨服服务器进行pk。那么就需要将player先从普通服务器ZS退到MS,由普通服务器的MS把player的数据发给跨服MS,最后再由跨服MS发送到跨服的ZS。如果还涉及DB则会更加的复杂。ZS、MS、DB相互独立,如果没有强制规定,这种同步往往是没有方向的:player的最新的数据一般在ZS上,但是有些交互不能在ZS上完成,那么可以有两种选择:(1)ZS把player数据发给MS,由MS进行操作,操作过程中ZS不能修改player的相关数据。(2)把player的最新数据存盘,然后由MS读盘取player的数据然后进行操作,操作过程中ZS还不能改变player的相关数据。不管哪种方法开发的逻辑多了以后都不好维护,从此维护人员都变成怨妇…
刀剑Online应该采用的是第2中方式,把player放在Zone Server上。
如何处理玩家对象player和world的关系集中体现了服务器端的哲学,值得细细品味。处理两者的关系在本人看来有两种主要的模式:一种是以player的中心的“自我”模式;另一种是以游戏世界为中心的“上帝之手”模式。这两种模式的哲学主要体现在加锁模式上,接下来进行详细介绍:
(一)player的“自我”模式
“自我”模式顾名思义,就是player以自我为中心,什么事都要亲历亲为。以player使用物品为例:
如上图是一个完整的player使用物品的流程。“自我”模式体现在上图的(1)位置,在(1)中首先取到服务器中对应的player,加锁后用player调用自己的处理函数进行处理。由于(1)相当于处理客户端发来的请求的入口处,因此在这里进行加锁和解锁操作是十分合适的,此后的(2)~(5)player在调用自己的处理函数时,编程人员完全不需要考虑加锁的问题。
所谓的“自我”模式,其实就是指在服务器端对player的操作其实都是player自己去完成的,“我”自己去把事情做完。处理逻辑时“我”(自己的player实例)不会主动去锁住对方的player实例,因此一个player不能修改另一个player的数据,world也不能修改player的数据。world和player的关系是“独立”的,player身上记着world的信息(相当于“我属于哪个世界”),world里存放着player的实例,但是它们直接不能直接修改对方的数据。这么做是为了避免死锁,使得加锁解锁变得简单而统一(如上图)。
“自我”模式有加锁解锁的便利,但是一个网络游戏怎么可能player和player、player和world直接没有交互呢?交互就需要获得或修改对方的数据,遇到这样的问题“自我”模式怎么处理呢?请看下图:
如上图:client A向服务器发送“向玩家B发起攻击”的消息,服务器端client A对应的实例Player A收到消息后,发现需要与player B进行交互,根据“自我”模型的限定,player A不能直接修改player B的血量等信息,这时player A需要做的是将自己的信息打包成一个msg结构然后push_back到消息队列message queue并指定player B作为接收者。在message queue上将这个msg转发给player B前,会先调用lock(playerB)将B锁住,接着把msg传给player B进行处理,在处理完毕后再调用unlock(playerB)。lock和unlock都在统一的地方调用,加锁解锁十分简洁,playerB处理msg消息时不用关心任何加锁问题。在上图的(2)中,player B进行伤害计算后将伤害消息广播给周围的玩家。
注意:“自我”模式下,伤害计算是在受攻击方进行的。player之间、player和world之间都是通过消息传递进行交互的。
总结:
优点:“自我”模式的优点集中体现在加锁上,编程人员在编写具体逻辑时不用担心加锁问题,也不用费尽心思来避免死锁,因为加锁都在消息入口处统一做了,同时不会主动去对别的player实例进行加锁操作。使用这种模式时,最好把移动的相关逻辑独立出来使用不同的锁,以免照成过多的加锁冲突和等待。
缺点:“自我”模式的缺点也是十分明显的,每个player实例作为不同的“我”独立存在,需要交互时只能通过消息队列来传递信息,极大的增加了交流的成本(内存、CPU占用率都会增加)。一个player往往不能即时的取到别的player的数据,这样一来很多的计算都只能做延后处理,比如伤害计算就只能放在被攻击者的player上,使得很多需要做先验判断的技能实现起来变得复杂,这类技能只能靠释放技能的player,发送请求消息给其他player,然后再由其他player把自己的信息通过msg queue发给释放技能的player。异步处理方式往往会但来更多的烦恼——需要增加很多错误判断、错误处理以及超时处理等等。在线人数高的时候,message queue的容量以及所占用的内存也是需要考虑的问题。
引用狄更斯的:“It was the best of times, it was the worst of times”。本人依葫芦画瓢:“自我”模式是一种nb的设计,也是一种sb的设计…
(二)world的“上帝之手”模式
“上帝之手”模式是以world为中心(类似war3),以地图为单位进行划分,player、NPC、monster等游戏对象都隶属于world。在world的掌控之下,就像有一只上帝之手在拨弄着这些小玩意。以client A向服务器发送“向玩家B发起攻击”消息为例,服务器端的处理流程如下:
从上图中可以看出“上帝之手”模式的核心是world去完成这项任务:找到playerA和B,把他们都锁住,然后交给技能模块来进行伤害计算,最后把结果广播出去。整个过程就像在玩war3这样的RTS游戏,服务器就像一个神,以斜45度的上帝视角来观察所有的玩家。
“上帝之手”模式的设计难点在于加锁策略,因为需要对多个对象进行加锁,加解锁的顺序不当容易产生死锁。加锁策略有很多种,这里不做具体的展开。只介绍一种最常用的方法,即对加锁对象进行排序。游戏对象在产生时都会有一个唯一的id做标识,当lock ()函数能按照一种稳定的算法对这些id进行排序时,就可以避免死锁。比如对游戏对象进行分类:player、npc、monster等,先对分类进行排序,再对对象的id进行排序。需要注意的是MMORPG游戏经常需要进行合服操作,为了保证合服后player的id不会重复,需要对player_id进行一些规划。
总结:
优点:服务器程序扮演上帝的角色,可以获得几乎所有的信息,这对逻辑编写带来极大的便利。和“自我”模式一样,最好把移动的相关逻辑独立出来使用不同的锁,以免照成过多的加锁冲突和等待。“上帝之手”模式更容易进行整体规划,更容易实现模块化设计,降低程序的耦合度。
缺点:上帝也不是这么好演的,“上帝之手”模式对整体框架、接口设计、加锁策略等有更高的要求,同时对编程人员的要求也会更高。
最近闲着没事把云风的《开发笔记》看了个遍,希望能从大牛的开发轨迹中得到一些启发。但可能是因为本人level太低,一遍看下来还是云里雾里,不甚明白。没办法只好再看一遍,希望能对他们的服务器端架构有个简单的认识,这里同时做些笔记。
PS:本文是我个人对云风的开发笔记的读后感,可能会有很多错误,慎入!
----------------------------------------华丽丽的分割线-------------------------------
一、服务器划分原则
在现有的网络游戏服务器端架构中,多是以功能和场景来划分服务器结构的。负载均衡和集群暂且不在本文中讨论(bigworld、atlas)。服务器划分可以基于以下原则:
接下来我们来看看云风的服务器架构是如何处理好以上几点的。
图1 服务器架构(此图为本人猜测,可能有误)
二、运行时的玩家数据
网络游戏服务器程序一项重要的工作就是根据client发过来的数据包,在服务器端模拟玩家的行为操作并把这些行为广播出去。那么服务器程序在运行时就需要一些实体来保存玩家的数据,这些实体可以是一个类,也可以是一个线程,设计思想不同采用的实体差别也会很大。这里涉及服务器端设计的一个核心问题:运行时玩家数据的保存、修改及数据流向。
agent
云风通过抽象实体agent来处理单个client的服务请求,agent和client是1:1的关系(见图1)。agent是在gate程序后端,负责翻译、转发以及回应客户端发过来的请求。agent的主要工作内容见云风的《开发笔记 (1)")》。值得补充的是设计agent的主要优势是:
把对单个 client 服务的代码集中写在 agent 服务中。由 agent 再和内部其它服务沟通。数据加载使用共享内存的方案,由 agent 向持久化模块发出信号,做加载或纯盘处理,通过共享内存得到结构化数据块。[2]
agent相当于client在服务器上对应的实体,玩家的属性和数据只能由agent来修改,别的服务只有读权限。通过attach操作获得数据(attach可能是通过服务器通讯框架skynet,也有可能直接mmap到共享内存sharedb上以获得数据)。
agent的设计使得整个系统对玩家数据的修改只有一个输入点,数据流十分的明确,易于维护。虽然这种设计可能会照成数据的多次复制,但是带来的代码维护和查错上的便利是十分可观的。
如果把所有的agent放在同一个进程里,在编程该程序时还应该考虑到容错问题,比如说(1)使用C++编写这个程序,agent以类的形式存在,使用thread pool来处理收到的数据包,实际操作时thread的数量是会远远小于agent的数量的,数据包到达后会在队列里等待thread调用agent的逻辑来处理。这是一种比较常见的设计方法,但要注意的是由于agent都放在一个进程里,程序的健壮性要求很高,一个进程core则会导致全服玩家掉线。而使用C++编写也增加了宕进程的可能性………你懂的。(2)使用Java编写,对于这种“中心节点”式架构来说可能是更好的选择,起码不是因为一个玩家的误操作(可能使用外挂)导致全服玩家掉线。(3)云风似乎是使用lua coroutine来实现agent的相互隔离和协同工作的,这样可以减少单一agent失败对其他agent的影响(动态语言的好处)。
sharedb
sharedb在系统中的地位看上去像是database前端的cache,但就本人的理解sharedb的作用远不止是一个数据缓存。
和天龙八部的ShareMemory类似,sharedb也采用了定长的结构化数据(见《开发笔记 (6)")》),通过共享内存来实现进程间的数据共享。sharedb的存在使得游戏逻辑处理和数据保存逻辑得到很好的隔离,游戏逻辑不用关心后端的数据是如何保存的,只要sharedb挂上定期存盘的服务,在接口定义明确的情况下,后端到底采用什么样的数据库变得不是那么重要,从而降低了系统的耦合度。
三、服务器底层框架skynet
skynet的设计思想见《Skynet 设计综述》:
我希望我们的游戏服务器(但 skynet 不仅限于用于游戏服务器)能够充分利用多核优势,将不同的业务放在独立的执行环境中处理,协同工作。这个执行环境,最早的时候,我期望是利用 OS 的进程,后来发现,如果我们必定采用嵌入式语言,比如 Lua 的话,独立 OS 进程的意义不太大。Lua State 已经提供了良好的沙盒,隔离不同执行环境。而多线程模式,可以使得状态共享、数据交换更加高效。而多线程模型的诸多弊端,比如复杂的线程锁、线程调度问题等,都可以通过减小底层的规模,精简设计,最终把危害限制在很小的范围内。这一点,Skynet 最终花了不到 3000 行 C 代码来实现核心层的代码,一个稍有经验的 C 程序员,都可以在短时间理解,做维护工作。
做为核心功能,Skynet 仅解决一个问题:
把一个符合规范的 C 模块,从动态库(so 文件)中启动起来,绑定一个永不重复(即使模块退出)的数字 id 做为其 handle 。模块被称为服务(Service),服务间可以自由发送消息。每个模块可以向 Skynet 框架注册一个 callback 函数,用来接收发给它的消息。每个服务都是被一个个消息包驱动,当没有包到来的时候,它们就会处于挂起状态,对 CPU 资源零消耗。如果需要自主逻辑,则可以利用 Skynet 系统提供的 timeout 消息,定期触发。
Skynet 提供了名字服务,还可以给特定的服务起一个易读的名字,而不是用 id 来指代它。id 和运行时态相关,无法保证每次启动服务,都有一致的 id ,但名字可以。
本人感觉skynet像一个发布订阅的消息中间件(还没看源码,可能有误),这种基于服务的即插即用式的框架给服务器端带来很大的可扩展性,同时也使得各模块之间独立清晰,具有良好的可维护性。但是这里有个疑问,服务都以so的形式挂在skynet上,那么这些服务从哪里获取玩家、怪物、NPC等object的数据?是从skynet中获得还是直接从sharedb中获得,出于性能的考虑是不是要把skynet和sharedb部署在同一台物理主机上?这样一来就会增加设计和具体逻辑的耦合度。看了《Skynet 集群及 RPC》,感觉skynet上的服务是要通过skynet来获得玩家的数据,这样操作会不会导致数据被复制很多次,不知道最终的效率是否受到影响?
四、gate
满足服务器划分原则里的第一点:分离游戏中占用系统资源(cpu,内存,IO等)较多的功能,独立成服务器。
gate的主要工作可以参见本系列blog的第二篇《网络游戏服务器构架设计(二):刀剑Online - 连接负载服务器CLS》
五、场景管理器
主要用于管理静态场景和动态副本,比如agent登录时查询自己所在场景对应的服务器地址。
六、场景服务器
场景服务器的内容我没有从《开发笔记》中得到太多的信息(可能level太低),更多的是以功能模块的形式写,比如AOI。不过其中有一点比较新颖的是云风认为player的位置、动作状态,战斗数值状态等都是场景的一部份,应该保存在场景中而不是agent中。本节有所更正见(八)补充。
据我的猜测,场景服务器应该会负责:
需要注意的是场景服务器修改的一些数据应该以什么样的频率通知agent呢?比如player的位置信息,该信息是完全保存在场景数据里还是说agent里也有一份?
七、总结
本文是一篇云风《开发笔记》系列blog的读后感,所述内容均是本人的猜测,虽恐贻笑大方,但也希望能抛砖引玉。收笔忐忑ing!
八、补充:
(1) 云风在微薄上的回复是:“我们最终采用的是单进程多线程, 每线程上一个 lua state 的结构. sharedb 是用来线程间数据交换的. gate 和 sharedb 以及 loader 和 agent map 一样, 都是 skynet 下的独立服务, 以 so 挂接进去的. 后来的商品交易, 掉落品分配也是 skynet 下的服务. ”
(2) 关于场景服务器,云风已经给出完整的说明,见《开发笔记(14)")》
场景服务分成两个部分,一是副本管理器,二是地图服务。在角色数据上,记录有角色应该属于的地图。agent 向地图所属的副本管理器查询,得到他所属的地图服务地址。便可以把自己注册到具体地图上。
地图服务管理了所有其中的角色 id ,以及若干 npc 。他的义务在于把让这些 id 对应的 agent 相互了解。但具体逻辑则放在每个 agent 服务上。每个 agent 自己所属进程 attach 其它 id ,可以获取其他对象的状态。…
(3) 风哥在《开发笔记(25)")》中已经提到最终使用单进程多线程的模式。看来简单设计是有共识的:-)
一个大型的网落游戏服务器应该包含几个模块:网络通讯,业务逻辑,数据存储,守护监控(不是必须),其中业务逻辑可能根据具体需要,又划分为好几个子模块。
这里说的模块可以指一个进程,或者一个线程方式存在,本质上就是一些类的封装。
对于服务器的并发性,要么采用单进程多线程,要么采用多进程单线程的方式,说说两种方式的优缺点:
一、单进程多线程的服务器设计模式,只有一个进程,但一个进程包好多个线程:
网络通讯层,业务逻辑,数据存储,分别在独立的线程中,无守护进程。
优点:
1.数据共享和交换方便,使用全局变量或者单例就可以,数据存储方便。
2.单进程,服务器框架结构相对简单,编码容易。
缺点:
1.所有功能只能在单个物理服务器上,不能做成分布式。
2.不方便监控各个线程状态,容易死锁
3.一个线程出错,例如内存非法访问,栈空间被破坏,那么服务器进程就退出,所有玩家掉线,影响大。
二、多进程单线程的服务器设计模式,多个进程,每个进程只有一个线程:
网路通讯,业务逻辑,数据存储,守护进程,分别在不同的进程。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
监控各个线程状态,容易死锁
3.一个线程出错,例如内存非法访问,栈空间被破坏,那么服务器进程就退出,所有玩家掉线,影响大。
二、多进程单线程的服务器设计模式,多个进程,每个进程只有一个线程:
网路通讯,业务逻辑,数据存储,守护进程,分别在不同的进程。
[外链图片转存中…(img-NfreRTZc-1715731260795)]
[外链图片转存中…(img-ykI9CGt4-1715731260796)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。