查看: 803|回复: 0

几种经典的游戏服务端的架构模型的分析

[复制链接]

4783

主题

5079

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
18913

最佳新人活跃会员热心会员推广达人宣传达人灌水之王突出贡献优秀版主荣誉管理论坛元老

发表于 2020-2-23 13:59:50 | 显示全部楼层 |阅读模式
几种经典的游戏服务端的架构模型的分析 https://www.gmbbs.net/

大多数程序员都熟悉事件驱动,最常谈论的是它在图形界面编程中的应用。事实上,事件驱动程序也广泛应用于网络编程,并大规模部署在具有高连接和高吞吐量的游戏服务端程序中,例如http游戏服务端程序和ftp游戏服务端程序。与传统的网络编程方法相比,事件驱动可以大大减少资源占用,提高服务接收能力,提高网络传输效率。
对于本文中提到的游戏服务端模型,可以在搜索网络上找到许多实现代码。因此,本文将重点介绍和比较模型,而不是坚持显示和分析源代码。使用libev事件驱动库的游戏服务端模型将给出实现代码。
本文引用线程/时间图例,只是为了说明线程在每个io上确实有阻塞延迟,但它不能保证延迟比率和IO执行顺序的正确性。此外,本文中提到的接口只是作者熟悉的Unix/Linux接口,不推荐使用Windows接口。读者可以自己查阅相应的窗口界面。
1、阻塞网络编程接口
几乎所有的程序员都是从listen(),send(),recv()和其他接口开始接触网络编程的。使用这些接口可以轻松构建游戏服务端/客户端模型。
让我们假设我们想建立一个简单的游戏服务端程序,为单个客户端提供类似“一个问题,一个答案”的内容服务
1.简单的问答游戏服务端/客户端模型
几种经典网络游戏服务端体系结构模型的分析与比较
我们注意到大多数套接字接口都被阻塞了。所谓阻塞接口是指系统调用(通常是IO接口),它不返回调用结果,并允许当前线程一直阻塞,并且仅在系统调用获得结果或超时出错时才返回。
事实上,除非特别指定,否则几乎所有的输入输出接口(包括套接字接口)都被阻塞。这给网络编程带来了一个大问题。例如,当调用send()时,线程将被阻塞,在此期间,线程将无法执行任何操作或响应任何网络请求。这给多客户端和多服务逻辑的网络编程带来了挑战。此时,许多程序员可能会选择多线程来解决这个问题。
2、多线程游戏服务端程序
对于多客户端网络应用,最简单的解决方案是在游戏服务端端使用多线程(或多进程)。多线程(或多进程)旨在使每个连接都有一个单独的线程(或进程),这样任何一个连接的阻塞都不会影响其他连接。
没有使用多进程或多线程的特定模式。传统上,进程的成本比线程的成本高得多。因此,如果需要同时服务更多的客户,不建议使用多进程。如果单个服务执行器需要消耗更多的CPU资源,例如大规模或长时间的数据操作或文件访问,那么这个过程会更安全。通常,pthread_create()用于创建新线程,fork()用于创建新进程。
让我们假设上面提到的游戏服务端/客户端模型要求更高的要求,即游戏服务端同时为多个客户端提供问答服务。所以我们有以下模型。

2.多线程游戏服务端模型
几种经典网络游戏服务端体系结构模型的分析与比较
在上面的线程/时间图例中,主线程持续等待来自客户端的连接请求。如果存在连接,则会创建一个新线程,并在新线程中提供与前面示例相同的问答服务。
许多初学者可能不明白为什么一个套接字可以接受很多次。事实上,套接字设计者可能特意为多客户端的情况留下了伏笔,允许accept()返回一个新的套接字。下面是接受接口的原型:
int accept(int s,struct sockaddr *addr,sock len _ t * addrlen);
输入参数S是从套接字()继承的套接字句柄值,绑定()和侦听()。在执行bind()和listen()之后,操作系统已经开始在指定端口监听所有连接请求。如果有任何请求,连接请求将被添加到请求队列中。调用accept()接口从套接字的请求队列中提取第一个连接信息,并创建一个类似于的新套接字返回句柄。新套接字句柄是后续read()和recv()的输入参数。如果请求队列当前没有请求,accept()会阻止该状态,直到有请求队列。
上面提到的多线程游戏服务端模型似乎完美地解决了为多个客户端提供问答服务的需求,但事实并非如此。如果您想同时响应数百个连接请求,多线程或多进程将严重占用系统资源,降低系统对外响应的效率,并且线程和进程本身也更有可能假死进入。
许多程序员可能会考虑使用“线程池”或“连接池”。“线程池”旨在减少创建和销毁线程的频率。它维护合理数量的线程,并允许空闲线程承担新的执行任务。“连接池”维护连接的缓存池,尽可能重用现有的连接,并减少创建和关闭连接的频率。这两种技术都可以很好地降低系统开销,并广泛应用于许多大规模系统,如websphere、tomcat和各种数据库。
然而,“线程池”和“连接池”技术只能在一定程度上缓解频繁的IO接口调用造成的资源占用。此外,所谓的“池”总是有它的上限。当请求大大超过上限时,由“池”形成的系统对外界的响应并不比没有池的系统好多少。因此,“池”的使用必须考虑其所面临的反应规模,并根据反应规模调整“池”的大小。
在上面的例子中,为了响应成千上万甚至数万个同时发生的客户端请求,“线程池”或者“连接池”可以减轻一些压力,但是它不能解决所有的问题。
总之,多线程模型可以方便有效地解决小规模的服务请求,但是面对大规模的服务请求,多线程模型并不是最好的解决方案。在下一章中,我们将讨论使用非阻塞接口来尝试解决这个问题。
3.使用select()接口的事件驱动游戏服务端模型
大多数Unix/Linux支持选择功能,该功能用于检测多个文件句柄的状态变化。以下是选择接口的原型:fd _ zero (intfd,fd _ set * fds) fd _ set (intfd,fd _ set * fds) fd _ isset (intfd,fd _ set * fds) fd _ clr (intfd,fd_set* fds) int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout)
这里,fd_set类型可以简单地理解为一个按位标记句柄的队列。例如,如果要在fd_set中标记值为16的句柄,fd_set的第16位将标记为1。具体的设置和验证可以通过使用宏来实现,如设置和设置。Readfds、writefds和exceptfds都是select()函数的输入和输出参数。如果输入的readfds标记了句柄号16,select()将检测句柄号16是否可读。select()返回后,可以通过检查readfds是否标记了句柄号16来确定“可读”事件是否发生。此外,用户可以设置超时时间。
接下来,将重新模拟前面示例中从多个客户端接收数据的模型。
几种经典网络游戏服务端体系结构模型的分析与比较
图4 .使用select()接收数据模型
上述模型仅描述了使用select () inte从多个客户端同时接收数据的过程
图5 .使用select()的事件驱动游戏服务端模型
这里应该指出的是,客户端的connect()操作将在游戏服务端端触发一个“可读事件”,因此select()也可以从客户端检测connect()行为。
在上述模型中,关键点是如何动态维护select()的三个参数readfds、writefds和excepted FD。作为输入参数,readfds应该标记所有需要检测的“可读事件”的句柄,包括总是检测connect()的“父”句柄。同时,write FD和exceptfds应该标记所有需要检测的“可写事件”和“错误事件”的句柄(用FD_SET()标记)。
作为输出参数,select()捕获的所有事件的句柄值都保存在readfds、writefds和excepted FD中。程序员需要检查所有标志位(使用FD_ISSET()检查)来确定哪些句柄有事件。
上述模型主要模拟“一问一答”的服务流程。因此,如果select()发现一个句柄捕获了一个“可读事件”,游戏服务端程序应该及时进行recv()操作,根据接收到的数据准备要发送的数据,将相应的句柄值添加到writefds中,并为“可写事件”的下一个select()探测做准备。类似地,如果select()发现一个句柄捕获了一个“可写事件”,程序应该及时发送()并为下一个“可读事件”探测做好准备。下图描述了上述模型中的执行周期。
几种经典网络游戏服务端体系结构模型的分析与比较
图6 .一个执行周期
这个模型的特点是每个执行周期都会检测到一个或一组事件,一个特定的事件会触发一个特定的响应。我们可以将这个模型归类为“事件驱动模型”。
与其他模型相比,使用select()的事件驱动模型只使用一个线程(进程)来执行,占用的资源较少,不消耗太多的CPU,并且可以为多个客户端提供服务。如果你试图构建一个简单的事件驱动的游戏服务端程序,这个模型有一定的参考价值。
然而,这种模式仍然存在许多问题。
首先,select()接口不是实现“事件驱动”的最佳选择。因为当要探测的句柄值很大时,select()接口本身需要花很多时间轮询每个句柄。许多操作系统提供更高效的接口,如linux提供epoll,BSD提供kqueue,Solaris提供/dev/poll。如果需要实现更高效的游戏服务端程序,更推荐像epoll这样的接口。不幸的是,不同的操作系统有非常不同的epoll接口,所以很难使用类似epoll的接口来实现具有更好的跨平台能力的游戏服务端。
其次,该模型包括事件检测和事件响应。一旦事件响应的执行者很大,对整个模型将是灾难性的。在下面的例子中,巨大的执行单元1将直接导致响应事件2的执行单元延迟执行,并且在很大程度上降低了事件检测的及时性。
几种经典网络游戏服务端体系结构模型的分析与比较
图7 .使用select()的大型执行器对事件驱动模型的影响
幸运的是,有许多有效的事件驱动库可以屏蔽上述困难。常见的事件驱动库包括libevent库和libev库,作为libevent的替代。这些库将根据操作系统的特点选择最合适的事件检测接口,并添加信号等技术来支持异步响应,这使得这些库成为构建事件驱动模型的唯一选择。下一篇文章将介绍如何使用libev库代替select或epoll接口来实现一个高效稳定的游戏服务端模型。
4.使用事件驱动的libev游戏服务端模型
Libev是一个高性能的事件周期/事件驱动库。作为libevent的替代,它的第一个版本于2007年11月发布。libev的设计者声称Libev具有更快的速度、更小的尺寸和更多的功能,这已经在许多评估中得到证明。由于其良好的性能,许多系统开始使用libev库。本文将介绍如何使用Libev实现一个提供问答服务的游戏服务端。
(事实上,现有许多事件周期/事件驱动库,作者不打算推荐读者使用libev库,而只是为了说明事件驱动模型给网络游戏服务端编程带来的便利和好处。大多数事件驱动库都有与libev库相似的接口,只要理解一般原理,就可以灵活地选择合适的库。
类似于前一章的模型,libev还需要循环检测事件是否发生。Libev的循环由ev_loop结构表示,并由ev_loop()开始。void ev_loop( ev_loop* loop,int标志)
Libev支持八种事件类型,包括IO事件。输入输出事件以输入输出为特征,用输入输出初始化()函数初始化:
void ev_io_init(ev_io *io,回调,int fd,int事件)
初始化内容包括回调函数、待检测的句柄fd和待检测的事件、EV_READ表“可读事件”和EV_WRITE表“可写事件”。
现在,用户需要做的是在正确的时间从ev_loop中添加或删除一些ev_io。添加后,下一个周期将检查ev_io指定的事件是否已经发生。如果检测到事件,ev_loop将自动执行ev_io的回调()。如果ev_io已注销,则不再检测到相应的事件。
无论ev _循环是否启动,都可以添加或删除一个或多个ev_io。添加和删除的接口是ev_io_start()和ev_io_stop()。
void ev_io_start( ev_loop *loop,ev_io* io)
无效ev_io_stop( EV_A_*)
由此,我们可以很容易地得到下面的“问答”游戏服务端模型。由于没有考虑游戏服务端端主动终止机制,每个连接可以在任何时间保持,客户端可以自由选择退出时间。
几种经典网络游戏服务端体系结构模型的分析与比较
图8 .使用libev库的游戏服务端模型
上述模型可以接受任意数量的连接,并为每个连接提供完全独立的问答服务。有了libev提供的事件周期/事件驱动接口,上述模型有机会具有其他模型所不能提供的高效率、低资源占用、良好的稳定性和简单的编写等特点。
由于传统的网络游戏服务端、ftp游戏服务端和其他网络应用都有“一个问题,一个答案”的通信逻辑,上述使用libev库的“一个问题,一个答案”模型对构建类似的游戏服务端程序具有参考价值。此外,上述模型还为需要远程监控的应用提供了一种可行的实现方案。
5.摘要
本文主要讨论如何构建一个提供“一个问题,一个答案”的游戏服务端程序。它讨论了用阻塞套接字接口实现的模型、多线程模型、使用select()接口的事件驱动游戏服务端模型和使用libev事件驱动库的游戏服务端模型。文章比较了各种模型的优缺点,并通过比较得出结论:使用“事件驱动模型”可以实现更高效、更稳定的游戏服务端程序。本文所描述的各种模型可以为读者的网络编程提供参考价值。




【GM论坛[www.gmbbs.net]免责声明】
1、本站提供的所有资源仅供参考学习使用,版权归原著所有,禁止下载本站资源参与商业和非法行为,请在24小时之内自行删除!
2、本站所有内容均由互联网收集整理、网友上传,并且以计算机技术研究交流为目的,仅供大家参考、学习,不存在任何商业目的与商业用途。
3、若您需要商业运营或用于其他商业活动,请您购买正版授权并合法使用。 我们不承担任何技术及版权问题,且不对任何资源负法律责任。
4、论坛的所有内容都不保证其准确性,完整性,有效性。阅读本站内容因误导等因素而造成的损失本站不承担连带责任。
5、用户使用本网站必须遵守适用的法律法规,对于用户违法使用本站非法运营而引起的一切责任,由用户自行承担
6、本站所有资源来自互联网转载,版权归原著所有,用户访问和使用本站的条件是必须接受本站“免责声明”,如果不遵守,请勿访问或使用本网站
7、本站使用者因为违反本声明的规定而触犯中华人民共和国法律的,一切后果自己负责,本站不承担任何责任。
8、凡以任何方式登陆本网站或直接、间接使用本网站资料者,视为自愿接受本网站声明的约束。
9、本站以《2013 中华人民共和国计算机软件保护条例》第二章 “软件著作权” 第十七条为原则:为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。若有学员需要商用本站资源,请务必联系版权方购买正版授权!
10、本网站如无意中侵犯了某个企业或个人的知识产权,请告之,本站将立即删除。
   提问发帖求助请点此发帖 https://www.gmbbs.net/
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表