传奇服务端打开异常出现服务器和服务端启动异常!!!

在启动的时候,你使用的引擎控制器启动的,这个引擎控制器在中间黑框中显示的你问题中的信息。

这个引擎控制器会启动以下几个部件:

并在启动这些部件后,监视这些部件,如果这些部件哪个自己关闭了,它就会把它重新启动起来。

出现你上面的错误信息,可能是:

1.你手动关了某个部件窗口

2.杀毒软件把你的某个部件给报成病毒了,被禁止启动或删除了。这个情况的验证方法为,重新下载服务器端,然后在运行前关闭杀毒软件,再运行服务器。我知道的就是360杀毒在默认情况下会报毒(其实不是毒)

}

传奇源码分析-服务器端

一个异步过程调用排队到此线程。 

我们不停地发出异步的WSASend/WSARecv IO操作,具体的IO处理过程由WINDOWS系统完成,WINDOWS系统完成实际的IO处理后,把结果送到完成端口上(如果有多个IO都完成了,那么就在完成端口那里排成一个队列)。我们在另外一个线程里从完成端口不断地取出IO操作结果,然后根据需要再发出WSASend/WSARecv IO操作。

LoginGate(登录网关服务器),接受客户端连接,并且把用户ID,密码直接发送到LoginSvr服务器中,由LoginSrv服务器验证之后,发送数据包返回给客户端。LoginGate之间是通过定时器,定时发送“心跳”数据。验证服务器存活的。客户端与服务器端的数据在传输中,是进行过加密的。

功能:(1)通过数据库的直接连接验证用户名密码正确;

(3)如果失败,返回错误,如果成功,同时返回server_list.

case 'X': 处理每个网关收到客户端Socket关闭之后发送过来的消息。设置该网关socket相应状态。

收到loginGate服务器发送过来的消息之后,ServerWorkerThread经过数据包分析之后(case 'A'),把客户端的消息,写入g_SendToGateQ队列中,然后在本线程中再进行处理。

SelGate服务器列表消息(对应TBL_SERVERINFO数据库表中数据),供用户选择登录的SelGate服务器。

遍历xUserInfoList用户列表信息,根据socket,找到用户密钥,消息解密后,遍历g_xGameServerList列表,把用户选择的SelGate服务器转化为IP地址,发送至LoginGate服务器,再转发至客户端。设置该用户SelServer的标志状态。从该网关的xUserInfoList用户列表中删除该用户。

LoginGate服务器,转发至客户端。

注:客户端从LoginSvr服务器得到SelGate服务器IP之后,连接SelGate服务器,进行角

色创建,删除,选择操作,然后发送数据到DBSrv服务器。

FD_READ事件读到的DBSrv服务器发来的数据)投递I/O,利用IOCP模型,发送到客户端。SleepEx挂起线程,至到一个I/O 完成回调函数被调用。一个异步过程调用排队到此线程。

投递,利用IOCP发送给客户端。

接收DBSrv服务器发送的数据包(心跳,登陆验证,selCur服务器地址),把数据加入缓冲区(g_xMsgQueue)中。

客户端Socket值赋值给结构体。记录客户相关信息。

socket,发相应数据包(异常)到DBSrv服务器(X命令-数据包),关闭客户端套按字。

如果数据验证正确,就转发数据包(A命令-数据包) DBSrv服务器。

IO操作,具体的IO处理过程由WINDOWS系统完成,WINDOWS系统完成实际的IO处理后,把结果送到完成端口上(如果有多个IO都完成了,那么就在完成端口那里排成一个队列)。我们在另外一个线程里从完成端口不断地取出IO操作结果,然后根据需要再发出WSASend/WSARecv

总结:SelGate(角色处理服务器),接受客户端连接,并且把用户数据包(角色处理)发送到DBSrv服务器中,由DBSrv服务器处理之后,发送数据包返回给客户端。SelGate之间是通过定时器,定时发送心跳数据。验证服务器存活的。客户端与服务器端的数据在传输中,是进行过加密的。


传奇文件类型格式探讨()

Wix文件:索引文件,根据索引查找到相应数据地址(数据文件)

我们下载一个Hedit编辑器打开一个Wil文件,分析一下。我们发现Wix文件中,0x23地址(含该地址)以前的内容是都相同的,即为:#INDX

0x30的地方:存放着38 04 00 00,高低位转换后为:0x438 = 1080, 这个就是图象数据的开始位置。

我们用Wil编辑打开对应的Wil文件,发现,果然有11张图片。另外我们发现,在Ofs = 44 -47之间的数据总是38 04 00 00,终于明白,所有的图片起始位置是相同的。

Wil文件: 数据文件。

前面我们说了图象数据的开始位置为0x438 = 1080, 1080中有文件开头的44字节都是相同的。所以,就是说有另外的1036字节是另有用途。1036中有1024是一个256色的调色板。而Wil里面的图片格式都是256色的位图储存。

FF为固定值(标识)。图片起始位置为:

为了验证数据是否正确,我们通过Wil工具,把第一幅图片导出来,然后用Hedit编辑器打开,经过对比,我们发现,数据一致。大小一致。

我们用Wil抓图工具打开看一下(确定是800*600大小)

我们导出第二张BMP图片

图片的大小为:496* 361, 我们从Wix中读出第二张图片的索引位置:

根据贴图,我们发现第二张图片的索引位置为: 40 57 07 00,转换为十六进制:0x75740,即为:481088,前面我们讲到第一张图片的结束位置是: 0fs 481077,Wix中读出来的也刚好为第二张图片的起始位置:

我们用工具打开第二张BMP图片,从起始位置,一直选取中至结束,发现刚好选496* 361字节大小。两边数据对比之后发现一致。知道了图片格式,我们可以写一个抓图片格式的程序了。

b. 加载物品,个人设置,魔法等。

RM_TURN消息。以玩家自己为中心,以24*24的区域里,向这个区域所属的块里的所有玩家列表发送消息)广播 AddProcess

一旦通过验证,就从验证列表中该玩家,改变玩家状态,LoadPlayer加载用户资源(地图中加入用户信息,向用户24*24区域内的块内玩家发送上线消息GameSrv广播新玩家上线(坐标)的消息。向该新玩家发送玩家信息(等级,装备,魔法,攻击力等)。

5.接受登录成功后,接收GameSrv服务器发送的消息:

调用OnSocketMessageRecieve函数。这个函数里面详细处理了客户端的游戏执行逻辑。如果是‘+’开头(数据包)则调用OnProcPacketNotEncode处理这种类型数据包。否则得到_TDEFAULTMESSAGE数据包,进行游戏逻辑处理。

SM_NEWMAP: 用户登录后,服务器发送的初始化地图消息。

SM_LOGON: 用户登录消息(服务器处理后返回结果)。用户登录成功后,在本地创建游戏对象,并发送消息,请求返回用户物品清单(魔法,等级,物品等)。

SM_ABILITY:服务器发送的本玩家金钱,职业信息。

SM_SUBABILITY : 服务器发送的玩家技能(魔法,杀伤力,速度,毒药,中毒恢复,生命恢复,符咒恢复)

客户端连接到GameGate游戏网关服务器,并通过GameSrv服务器验证之后,就会收到GameSrv服务器发来的消息。主要是地图消息,登录消息,玩家的装备,技能,魔法,个人设置等等。GameSrv把地图分成若干块,把该玩家加入其中一块,并加入这一块的用户对象列表中,设置其状态为OS_MOVINGOBJECT。客户端加载地图,设置场景,设置自己的玩家状态(此时还没有怪物和其它玩家,所以玩家还需要接收其它游戏玩家和怪物的清单列表)。

6. 接收怪物,商人,其它玩家的消息:

遍历g_pMerchantInfo结构(根据nNumOfMurchantInfo数量)。得到商人类型相关的地图,创建商人对象,设置不同的编号,坐标,头像及所属地图。在该地图中加入该商人,且在g_xMerchantObjList商人清单中加入该商人。

客户端收到消息后相应的处理:

c. SM_STRUCK 处理受攻击(本玩家,或者其它的玩家,NPC等)。

候由服务器发送的用户消息获取的。

如果是服务器端游戏玩家自己发送的消息,则处理自己的消息。否则如果是其它玩家(怪物)发送的消息,遍历m_xActorList列表, 判断该对象是否存在,如果该不存在,则根据stFeature.bGender的类型

根据服务器发送的消息,(创建一个虚拟玩家NPC,怪物,在客户端),根据参数,初始化该对象设置(方向,坐标,名字,等级等)。在后面的处理中绘制该对象到UI界面中()

下面分析一下用户登录之后的流程:

从前面的分析中可以看到,该用户玩家登录成功之后,得到了服务器发送来的各种消息。处理也比较复杂,同时有一定的优先级处理。并且根据用户登录后的XY坐标,向用户发送来了服务器XY坐标为中心附近单元格中的所有玩家(NPC,怪物)SM_TURN消息。

客户端根据数据包的标志,创建这些NPC,设置属性,并且把它们加入m_xActorList对列中。最后在UI界面上绘制这些对象。

现在假设玩家开始操作游戏:

传奇的客户端源代码工程WindHorn

当用户进行游戏之后,点击鼠标左键,来处理玩家走动的动作:

客户端执行流程:(玩家走动)

消息,隐藏该物体,GoldChanged(),改变玩家的金钱。否则,把黄金返回地图中。

先判断m_xPacketQueue是否有数据,有则先处理。返回。

游戏服务器执行流程:(玩家走动)

判断玩家if (!m_fIsDead),如果已死,则发送_MSG_FAIL消息。我们在前面看到过,该消息是被优先处理的。否则则调用WalkTo,并发送_MSG_GOOD消息给客户端。

AddRefMsg函数,我们在后面的服务器代码里分析过:它会根据XY坐标,在以自己坐标为中心周围26*26区域里面,按地图单元格的划分,遍历所有单元格,再遍历所有单元格内的玩家列表,广播发送RM_WALK消息。

客户端执行流程:(反馈服务器端本玩家走动)

其它客户端执行流程:(反馈服务器端其它玩家)

最近对高性能的服务器比较感兴趣,读过了DELPHISocker源码WebServiceRemObject之后,高性能的服务器感兴趣。

你可能需要的以下知识才能更好的读懂一个商业源码:

2).面向对象技术的熟悉掌握。

5).一门熟悉的开发工具掌握,和多种语言的源码阅读能力。

SelGate七个工程文件。传奇的客户端源代码有两个工程,WindHornMir2Ex

另外下载了乐都WIL编辑器和乐都MPA地图编辑器这些工具

Clear函数中调用在后台缓存上进行绘图操作,换页至屏幕。

Wix文件:索引文件,根据索引查找到相应数据地址(数据文件)

我们下载一个Hedit编辑器打开一个Wil文件,分析一下。我们发现Wix文件中,0x23地址(含该地址)以前的内容是都相同的,即为:#INDX

0x30的地方:存放着38 04 00 00,高低位转换后为:0x438 = 1080, 这个就是图象数据的开始位置。

我们用Wil编辑打开对应的Wil文件,发现,果然有11张图片。另外我们发现,在Ofs = 44 -47之间的数据总是38 04 00 00,终于明白,所有的图片起始位置是相同的。

Wil文件: 数据文件。

前面我们说了图象数据的开始位置为0x438 = 1080, 1080中有文件开头的44字节都是相同的。所以,就是说有另外的1036字节是另有用途。1036中有1024是一个256色的调色板。

为了验证数据是否正确,我们通过Wil工具,把第一幅图片导出来,然后用Hedit编辑器打开,经过对比,我们发现,数据一致。大小一致。

知道了图片格式,我们可以写一个抓图片格式的程序了。

传奇的客户端源代码有两个工程,WindHornMir2Ex

  初始化声音,加载Socket库之后,进行CWHDefProcess*指针赋值(事件绑定)g_bProcState变量反应了当前游戏的状态(登录,角色选择,游戏逻辑处理)。调用Load初始化一些操作(登录,角色选择,游戏逻辑处理)。进行消息循环。

 2.接收处理网络消息和接收处理窗口消息。

在不同的状态下(登录,角色选择,游戏逻辑处理),接收到的消息(网络,窗口消息)会分派到不同的函数中处理的。这里是用虚函数处理(调用子类方法,由实际的父类完成相应的处理)

I/O,异步I/O模型。异步I/O和同步I/O不同,同步I/O时,程序被挂起,一直到I/O处理完,程序才能获得控制。异步I/O,调用一个函数告诉OS,进行I/O操作,不等I/O结束就立即返回,继续程序执行,操作系统完成I/O之后,通知消息给你。Overlapped I/O只是一种模型,它可以由内核对象(hand),事件内核对象(hEvent), 异步过程调用(apcs) 和完成端口(I/O

     取代多线程功能,(多线程存在同步机制,错误处理,在成千上万个线程I/O中,线程上下文切换是十分消耗CPU资源的)。

     Overlapped I/O模型是OS为你传递数据,完成上下文切换,在处理完之后通知你。由程序中的处理,变为OS的处理。内部也是用线程处理的。

程设置这个成员,读写命名管道及通信设备时调用进程忽略这

信设备时调用进程忽略这个成员;

,调用进程设置这个成员. 相关函数 

2 程序和系统之间提供了共享区域。参数可以在区域内双向传递。

在请求时,不能释放,只有在I/O请求完成之后,才可以释放。如果发出多个overlapped请求,每个overlapped读写操作,都必须包含文件位置(socket),另外,如果有多个磁盘,I/O执行次序无法保证。(每个overlapped都是独立的请求操作)。

内核对象(hand)实现:

例子:用overlapped模型读一个磁盘文件内容。

异步I/O字节位置必须在OVERLAPPED结构中指定。

内核对象(hand)实现的问题:

    不能区分那一个overlapped操作,对同一个文件handle,系统有多个异步操作时(一边读文件头,一边写文件尾, 有一个完成,就会有信号,不能区分是那种操作。),为每个进行中的overlapped调用GetOverlappedResult是不好的作法。

事件内核对象(hEvent)实现方案:

Overlapped成员hEven标识事件内核对象。CreateEvent,为每个请求创建一个事件,初始化每个请求的hEvent成员(对同一文件多个读写请求,每个操作绑定一个event对象)。调用WaitForMultipleObject来等等其中一个(或全部)完成。

WaitForSingleObject() WaitForMultipleObjects()会等待事件到信号状态,随后又自动将其重置为非信号状态,这样保证了等待此事件的线程中只有一个会被唤醒。

需要用户调用ResetEvent()才会重置事件。可能有若干个线程在等待同一事件,这样当事件变为信号状态时,所有等待线程都可以运行了。 SetEvent()函数用来把事件对象设置成信号状态,ResetEvent()把事件对象重置成非信号状态,两者均需事件对象句柄作参数。

异步过程调用(apcs)

事件内核对象(hEvent)的问题:

异步过程调用(apcs)实现方案:

I/O完成之后,系统调用该回调函数。OS在有信号状态下(设备句柄),才会调用回调函数(可能有很多APCS等待处理了),传给它完成I/O请求的错误码,传输字节数和Overlapped结构的地址。

异步过程调用(apcs)问题:

    不会限制handle个数,可处理成千上万个连接。I/O completion port允许一个线程将一个请求暂时保存下来,由另一个线程为它做实际服务。

在典型的并发模型中,服务器为每一个客户端创建一个线程,如果很多客户同时请求,则这些线程都是运行的,那么CPU就要一个个切换,CPU花费了更多的时间在线程切换,线程确没得到很多CPU时间。到底应该创建多少个线程比较合适呢,微软件帮助文档上讲应该是2*CPU个。但理想条件下最好线程不要切换,而又能象线程池一样,重复利用。I/O完成端口就是使用了线程池。

在我们使用完成端口之前,要调用CreateIoCompletionPort函数先创建完成端口对象。

可以用来和完成端口联系的各种句柄,文件,套接字)

用户自定义数值,被交给服务的线程。GetQueuedCompletionStatus函数时我们可以完全得到我们在此联系函数中的完成键(申请的内存块)。在GetQueuedCompletionStatus

中可以完封不动的得到这个内存块,并且使用它。

参数NumberOfConcurrentThreads用来指定在一个完成端口上可以并发的线程数量。理想的情况是,一个处理器上只运行一个线程,这样可以避免线程上下文切换的开销。如果这个参数的值为0,那就是告诉系统线程数与处理器数相同。我们可以用下面的代码来创建I/O完成端口。

隐藏在之创建完成端口的秘密:

1 创建一个完成端口

2 设备列表,完成端口把它同一个或多个设备相关联。

根据处理器个数,创建cpu*2个工作线程:

)把一个套接字句柄和一个完成端口绑定到一起。完成端口又同一个或多个设备相关联着,所以以套接字为基础,投递发送和请求,对I/O处理。接着,可以依赖完成端口,接收有关I/O操作完成情况的通知。再看程序里:

当一个设备的异步I/O请求完成之后,系统会检查该设备是否关联了一个完成端口,如果是,系统就向该完成端口的I/O完成队列中加入完成的I/O请求列。

然后我们需要从这个完成队列中,取出调用后的结果(需要通过一个Overlapped结构来接收调用的结果)。怎么知道这个队列中已经有处理后的结果呢,调用GetQueuedCompletionStatus函数。

和异步过程调用不同(在一个Overlapped I/O完成之后,系统调用该回调函数。OS在有信号状态下(设备句柄),才会调用回调函数(可能有很多APCS等待处理了))

CompletionPort:指出了线程要监视哪一个完成端口。很多服务应用程序只是使用一个I/O完成端口,所有的I/O请求完成以后的通知都将发给该端口。

完成端口的单句柄数据指针,这个指针将可以得到我们在CreateIoCompletionPort中申请那片内存。

重叠I/O请求结构,这个结构同样是指向我们在重叠请求时所申请的内存块,同时和lpCompletionKey,一样我们也可以利用这个内存块来存储我们要保存的任意数据。

     等待线程队列很简单,只是保存了这些线程的ID。完成端口会按照后进先出的原则将一个线程队列的ID放入到释放线程列表中。

这样,I/O完成端口内核对象就知道哪些线程正在等待处理完成的I/O请求。当端口的I/O完成队列出现一项时,完成端口就唤醒(睡眠状态中变为可调度状态)等待线程队列中的一个线程。线程将得到完成I/O项中的信息:传输的字节数,完成键(单句柄数据结构)Overlapped结构地址,线程是通过GetQueuedCompletionStatus返回这些信息,等待CPU的调度。

深入分析完成端口线程池调度原理:

假设我们运行在2CPU的机器上。创建完成端口时指定2个并发,创建了4个工作线程加入线程池中等待完成I/O请求,且完成端口队列(先入先出)中有3个完成I/O的请求的情况:

创建了4个工作线程,调用GetQueuedCompletionStatus时,该调用线程就进入了睡眠状态,假设这个时候,I/O完成队列出现了三项,调用线程的ID就被放入该等待线程队列中,

I/O完成端口内核对象(第3个参数等级线程队列),因此知道哪些线程正在等待处理完成的I/O请求。当端口的I/O完成队列出现一项时,完成端口就唤醒(睡眠状态中变为可调度状态)等待线程队列中的一个线程(前面讲过等待线程队列是后进先出)。所以线程D将得到完成I/O项中的信息:传输的字节数,完成键(单句柄数据结构)Overlapped结构地址,线程是通过GetQueuedCompletionStatus返回这些信息。

在前面我们指定了并发线程的数目是2,所以I/O完成端口唤醒2个线程,线程D和线程C,另两个继续休眠(线程B,线程A),直到线程D处理完了,发现表项里还有要处理的,就唤醒同一线程继续处理。

    并发量限制了与该完成端口相关联的可运行线程的数目, 它类似阀门的作用。 当与该完成端口相关联的可运行线程的总数目达到了该并发量,系统就会阻塞任何与该完成端口相关联的后续线程的执行, 直到与该完成端口相关联的可运行线程数目下降到小于该并发量为止。所以解释了线程池中的运行线程可能会比设置的并发线程多的原因。

最有效的假想是发生在有完成包在队列中等待,而没有等待被满足,因为此时完成端口达到了其并发量的极限。此时,一个正在运行中的线程调用 GetQueuedCompletionStatus时,它就会立刻从队列中取走该完成包。这样就不存在着环境的切换,因为该处于运行中的线程就会连续不断地从队列中取走完成包,而其他的线程就不能运行了

注意:如果池中的所有线程都在忙,客户请求就可能拒绝,所以要适当调整这个参数,获得最佳性能。

线程并发:D线程挂起,加入暂停线程,醒来后又加入释放线程队列。

PostQueudCompletionStatus函数,我们可以用它发送一个自定义的包含了OVERLAPPED成员变量的结构地址,里面包含一个状态变量,当状态变量为退出标志时,线程就执行清除动作然后退出。

完成端口使用需要注意的地方:

99年始于校园中的项目,几个人出于爱好做了这样一个应用,以实现以下功能:

  • 聚合,把朋友的文章聚合在一起

LiveJournal采用了大量的开源软件,甚至它本身也是一个开源软件。

在上线后,LiveJournal实现了非常快速的增长:

  • 20044月份:280万注册用户。
  • 20054月份:680万注册用户。
  • 20058月份:790万注册用户。
  • 达到了每秒钟上千次的页面请求及处理。
  • 使用了大量MySQL服务器。

LiveJournal1台服务器发展到100台服务器,这其中经历了无数的伤痛,但同时也摸索出了解决这些问题的方法,通过对LiveJournal的学习,可以让我们避免LJ曾经犯过的错误,并且从一开始就对系统进行良好的设计,以避免后期的痛苦。

下面我们一步一步看LJ发展的脚步。

一台别人捐助的服务器,LJ最初就跑在上面,就像Google开始时候用的破服务器一样,值得我们尊敬。这个阶段,LJ的人以惊人的速度熟悉的Unix的操作管理,服务器性能出现过问题,不过还好,可以通过一些小修小改应付过去。在这个阶段里LJCGI升级到了FastCGI

最终问题出现了,网站越来越慢,已经无法通过优过化来解决的地步,需要更多的服务器,这时LJ开始提供付费服务,可能是想通过这些钱来购买新的服务器,以解决当时的困境。
毫无疑问,当时LJ存在巨大的单点问题,所有的东西都在那台服务器的铁皮盒子里装着。

用付费服务赚来的钱LJ买了两台服务器:一台叫做KennyDell 6U机器用于提供Web服务,一台叫做CartmanDell 6U服务器用于提供数据库服务。

LJ有了更大的磁盘,更多的计算资源。但同时网络结构还是非常简单,每台机器两块网卡,Cartman通过内网为Kenny提供MySQL数据库服务。

暂时解决了负载的问题,新的问题又出现了:

  • 原来的一个单点变成了两个单点。
  • 网站速度慢的问题又开始出现了,没办法,增长太快了。
  • Web服务器上CPU达到上限,需要更多的Web服务器。

又买了两台,KyleStan,这次都是1U的,都用于提供Web服务。目前LJ一共有3Web服务器和一台数据库服务器。这时需要在3Web服务器上进行负载均横。

  • 单点故障。数据库和用于做网关的Web服务器都是单点,一旦任何一台机器出现问题将导致所有服务不可用。虽然用于做网关的Web服务器可以通过保持心跳同步迅速切换,但还是无法解决数据库的单点,LJ当时也没做这个。
  • 网站又变慢了,这次是因为IO和数据库的问题,问题是怎么往应用里面添加数据库呢?

又买了一台数据库服务器。在两台数据库服务器上使用了数据库同步(Mysql支持的Master-Slave模式),写操作全部针对主数据库(通过Binlog,主服务器上的写操作可以迅速同步到从服务器上),读操作在两个数据库上同时进行(也算是负载均横的一种吧)

实现同步时要注意几个事项:

  • 读操作数据库选择算法处理,要选一个当前负载轻一点的数据库。
  • 在从数据库服务器上只能进行读操作
  • 准备好应对同步过程中的延迟,处理不好可能会导致数据库同步的中断。只需要对写操作进行判断即可,读操作不存在同步问题。

有钱了,当然要多买些服务器。部署后快了没多久,又开始慢了。这次有更多的Web服务器,更多的数据库服务器,存在 IOCPU争用。于是采用了BIG-IP作为负载均衡解决方案。

现在服务器基本上够了,但性能还是有问题,原因出在架构上。

数据库的架构是最大的问题。由于增加的数据库都是以Slave模式添加到应用内,这样唯一的好处就是将读操作分布到了多台机器,但这样带来的后果就是写操作被大量分发,每台机器都要执行,服务器越多,浪费就越大,随着写操作的增加,用于服务读操作的资源越来越少。

现在我们发现,我们并不需要把这些数据在如此多的服务器上都保留一份。服务器上已经做了RAID,数据库也进行了备份,这么多的备份完全是对资源的浪费,属于冗余极端过度。那为什么不把数据分布存储呢?

问题发现了,开始考虑如何解决。现在要做的就是把不同用户的数据分布到不同的服务器上进行存储,以实现数据的分布式存储,让每台机器只为相对固定的用户服务,以实现平行的架构和良好的可扩展性。

为了实现用户分组,我们需要为每一个用户分配一个组标记,用于标记此用户的数据存放在哪一组数据库服务器中。每组数据库由一个master及几个slave组成,并且slave的数量在2-3台,以实现系统资源的最合理分配,既保证数据读操作分布,又避免数据过度冗余以及同步操作对系统资源的过度消耗。

由一台(一组)中心服务器提供用户分组控制。所有用户的分组信息都存储在这台机器上,所有针对用户的操作需要先查询这台机器得到用户的组号,然后再到相应的数据库组中获取数据。

这样的用户架构与目前LJ的架构已经很相像了。

在具体的实现时需要注意几个问题:

  • 在数据库组内不要使用自增ID,以便于以后在数据库组之间迁移用户,以实现更合理的I/O,磁盘空间及负载分布。
  • useridpostid存储在全局服务器上,可以使用自增,数据库组中的相应值必须以全局服务器上的值为准。全局服务器上使用事务型数据库InnoDB
  • 在数据库组之间迁移用户时要万分小心,当迁移时用户不能有写操作。
  • 一个全局主服务器,挂掉的话所有用户注册及写操作就挂掉。
  • 每个数据库组一个主服务器,挂掉的话这组用户的写操作就挂掉。
  • 数据库组从服务器挂掉的话会导致其它服务器负载过大。

对于Master-Slave模式的单点问题,LJ采取了Master-Master模式来解决。所谓Master-Master实际上是人工实现的,并不是由MySQL直接提供的,实际上也就是两台机器同时是Master,也同时是Slave,互相同步。

  • 一个Master出错后恢复同步,最好由服务器自动完成。
  • 数字分配,由于同时在两台机器上写,有些ID可能会冲突。
  • 奇偶数分配ID,一台机器上写奇数,一台机器上写偶数
  • 通过全局服务器进行分配(LJ采用的做法)

Master-Master模式还有一种用法,这种方法与前一种相比,仍然保持两台机器的同步,但只有一台机器提供服务(读和写),在每天晚上的时候进行轮换,或者出现问题的时候进行切换。

  • 需要做更多的配置,不过值得,可以更安全的存储数据,以及得到更快的速度。
  • 记录日志(LJ用它来记网络访问日志)
  • 存储只读静态数据,足够快。
  • 并发性很差,无法同时读写数据(添加数据可以)
  • MySQL非正常关闭或死机时会导致索引错误,需要使用myisamchk修复,而且当访问量大时出现非常频繁。

去年我写过,它就是由LJ的团队开发的一款缓存工具,以key-value的方式将数据存储到分布的内存中。LJ缓存的数据:

  • 12台独立服务器(不是捐赠的)
  • 90-93%的命中率(用过squid的人可能知道,squid内存加磁盘的命中率大概在70-80%

想缓存所有的东西?那是不可能的,我们只需要缓存已经或者可能导致系统瓶颈的地方,最大程度的提交系统运行效率。通过对MySQL的日志的分析我们可以找到缓存的对象。

  • 没有完美的事物,缓存也有缺点:
  • 增大开发量,需要针对缓存处理编写特殊的代码。
  • 管理难度增加,需要更多人参与系统维护。

10Web访问负载均衡

在数据包级别使用BIG-IP,但BIG-IP并不知道我们内部的处理机制,无法判断由哪台服务器对这些请求进行处理。反向代理并不能很好的起到作用,不是已经够快了,就是达不到我们想要的效果。

所以,LJ又开发了。特点:

  • 快,小,可管理的http web 服务器/代理
  • 支持Console管理与http远程管理,支持动态配置加载
  • 多种模式:web服务器,反向代理,插件
  • 支持插件:GIF/PNG互换?

LJ使用开源的作为分布式文件存储系统。MogileFS使用非常简单,它的主要设计思想是:

  • 文件属于类(类是最小的复制单位)
  • 使用MySQL集群统一存储分布信息

到目前为止就这么多了,更多文档可以在找到。的同学们拿这个文档参加了两次MySQL Con,两次OS Con,以及众多的其它会议,无私的把他们的经验分享出来,值得我们学习。在web2.0时代快速开发得到大家越来越多的重视,但良好的设计仍是每一个应用的基础,希望web2.0们在成长为Top500网站的路上,不要因为架构阻碍了网站的发展。

}

我反映了问题,人格来说的是我设备自动清楚,喊我换个设备。

后来才想明白,真信这个真是我傻B

我设备是酷开50U2电视,我就打电话问了一下上次售后客服,以及上次教我弄系统的售后师傅。

客服比较客气,问,说格来既然说是被我们清了电视上信息或说缓存什么的,那么你优酷 腾讯 PPTV这些视频TV端账户怎么被清没?

售后那个师傅就比较直接了,难道我们电视专清格来的账户信息或者缓存,有必要?

另外还有个疑问,就是这类事情,基本发生在下午 晚上高峰期,上午 深夜人少的时候出现这些情况就极少极少,难道大家都开会员 超级会员什么的,我就长的丑点,只能昼伏夜出?

这会七点半,游戏就进步了,拍照给你们看,然后晚点 深夜 上午我分别试试,发图给你们看看,格来所谓的账号信息缓存被我自己电视清除的说法是啥情况,感情我这电视是超级智能,白天清扫 晚上休息,咋不早九晚五呢?


「炫舞时代」四周年,历史超强回馈,千万好礼等你领!双人座椅,光效情侣套,全商城免费等福利同庆四周年,还有服饰精染,炫宝儿旅行等全新玩法等你来体验!










华为云中国行2018-武汉站将于9月6日召开,技术大咖/行业领袖莅临现场,聚焦智造汽车工业,AI医药,AI科教等领域,共同探讨华为云生态建设,企业云上创新之路.点击报名参会!



你有安卓手机吧?你的手机上使用这个会不会出现同样的情况如果手机出现同样的问题就跟电视无关了你用手机测试测试手机没这个毛病的话八成还是电视的缘故





你的问题确实很怪 有一种可能是他们的程序漏洞不过这只是一种可能也不一定


我最近玩圣徒3频繁出现存档丢失的情况,虽然它没写啥都是玩过的关卡又要从新过一遍不爽,好打还好说问题是保护小弟之类的任务重新打超不爽,好在昨天通关了不会再玩了


你这个问题真的好奇怪,头一次碰见,但是你放心,技术会认真帮你查找原因并解决的
你能认真的发帖不断验证也证明你是热爱格来的,非常感谢你的支持!








详细说明一下 这个问题 我上报给了技术 查到的原因是这样的:就是酷开电视这个型号 因为型号的特殊性 只能共用一个设备码 (一般情况都是每个设备一个设备码 比如你的小米3和他的小米3 是不一样 但是类似的设备码 可是酷开电视所有设备 都是共同 一样的一个设备码) 所以 您这个电视型号 当你不能玩的时候 就是代码222的时候 产生这个的原因是因为有人用了同款设备在玩游戏 您这边就被踢了 至于您说您某些时候可以玩的 就是因为这个时间 没有人用这个设备玩
我们目前正在找其他方法解答这个问题 请您耐心等待
还有就是建议您最好使用任何其他设备 就不会产生想相同问题 感谢您对我们的支持!


我最后还是那句话如果真的是广泛性的问题。迟早都会解决的如因为这个群体都有这个问题。不过就不知道什么时候解决了。


}

我要回帖

更多关于 服务器和服务端 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信