精华内容
下载资源
问答
  • 在基于事件驱动的网络编程模型中,Reactor和Proactor模型两种常用的IO设计模型。 我们知道BIO(阻塞IO)只有等待阻塞方法结束了,操作权才会交还给调用线程,在阻塞期间,调用者做不了任何操作,只能等待。在此...

    我们在IO模型和Java网络编程模型中,对IO有了一定的理解。这一篇,主要讲解基于事件驱动的两种是在原来基础上的扩展。在基于事件驱动的网络编程模型中,Reactor和Proactor模型是两种常用的IO设计模型。

    我们知道BIO(阻塞IO)只有等待阻塞方法结束了,操作权才会交还给调用线程,在阻塞期间,调用者做不了任何操作,只能等待。在此期间调用者线程无法转向其他能做的事,对调用者来讲,这其实是一种浪费。在NIO(非阻塞IO)中,非阻塞IO会立刻将结果返回到调用者,调用者获取结果无需等待。获得的结果无非两种:数据准备好了,你继续表演吧;要么数据还没准备好,你要不再试试,或者你等会再试试。而对AIO(异步IO),调用方立刻获得返回,并且操作系统会使用另外的资源来达成此次的IO请求,并在操作系统完成数据准备后,通知调用者。

    Reactor模式和Proactor模型

    Reactor模型的中心思想采用的就是我们在IO模型和Java网络编程模型中所讲的多路复用IO。Reactor模型主要包含以下几个角色:

    1)Handle,也叫句柄,有些地方也叫描述符。如网络Socket IO中称网络IO句柄或者Socket描述符,在文件读写中称文件IO句柄或者文件描述符等。

    2)Synchronous Event Demultiplexer,也称同步事件多路分解器,用于阻塞等待发生在句柄集合上的一个或者多个事件,因为事件的到来是随机不可预测的,底层要实现事件的监听采取的是循环等待的策略,这种策略也叫事件循环。事件循环依赖系统调用,这里的系统调用,指的就是select/poll/epol等底层函数。这些底层函数一旦监听到句柄(或描述符)的读就绪或者写就绪,就会通知调用方进行读写操作。同步事件分离器依赖底层系统调用的实现。

    3)Event Handler, 可称为事件处理器,不同的事件,可以有不同的处理器。通常IO框架库会将事件处理器定义成一个模板函数,不同的事件处理器可以有不同的特性,拥有自己业务相关的实现。

    4)Reactor,也称为反应器,反应器主要功能包括以下:注册或者删除应用程序所关注的事件句柄,运行事件循环监控事件的到来,当事件到达时,反应器分离事件,并通知到对应的具体的事件处理器上,由具体的事件处理器处理器调用相关的函数来实现数据的读或者写,处理完数据后,交还系统的控制权。

    Reactor模型图

    整体的业务流可以概括为,首先调用者先向反应器注册,其对某种具体 的IO动作感兴趣,反应器会循环监控调用 者注册的事件是否已发生(如可读可写等),当事件发生后,事件分解器就被唤醒,会通知到事件对应的处理器来完成 事件的读写,可以看出,在具体事件到达时,处理程序不是调用的反应器,而是通过反应器分离的事件处理器来作数据的读或者写,这种方式又被称为“好莱坞法则”,类似于好莱坞大导演找演员的模型,你别来找我,等活来了我来找你吧。Reactor模型当有对应的IO完成时,回调对应的函数来处理,这种模型本质上还是一种同步的IO模型,其底层也并没有调用对应的异步IO的函数。

    Reactor模型中的事件处理器是基于模板的模型来实现的,这意味着不同的事件处理器是分离的,业务间具体低侵入性。Reactor模型的底层系统调用也是一个模板模型,可以用select实现,亦或epoll,但是必须满足能监控活跃连接的功能。

    Proactor与Reactor则不同,Reactor是基于同步IO模型实现的,而Proactor是基于异步IO。

    Proactor中,调用者会调用异步操作处理器提供的异步函数,调用之后, 调用者线程和异步操作处理器会独立运行,这依赖于操作系统对异步IO的支持,实际的IO是由操作系统来完成 的。然后Proactor会进行事件循环,等待事件的到来 ,当事件分解器等待事件到来的同时,操作系统已经在同时将目标数据拷备到用户空间,当拷备完成 后,会通知到事件分解器,事情已经搞完了,事件分解器将完成事件转发到相应的事件处理者或者 回调函数,调用 者就可以利用这些数据处理自己的业务逻辑 了。

    Reactor和Proactor都是对某个具体IO事件的告知,这也就我们所说的基于事件驱动,Reactor是告知的事件已准备好,而Proactor是事件已做好。他们的业务 逻辑也很类似,都是事件分解器来负责IO的监控,并回调对应的事件处理器,不同之处在于Reactor事件告知的是已准备好,还需要进一步的从内核 空间数据拷备到用户空间。而Proactor是数据已经准备好,调用 者可以直接在用户空间使用了。

           为了让学习变得轻松、高效,今天给大家免费分享一套Java入门教学资源。帮助大家在成为Java架构师的道路上披荆斩棘。需要入门的资料欢迎加入学习交流群:9285,05736

    展开全文
  • 1、服务器模型1.1C/S模型【1】C/S(客户端/服务器)模型:所有客户端都通过访问服务器来获取所需的资源【2】C/S模型逻辑:服务器启动后,先创建一个或者多个监听socket,并调用bind函数将其绑定到服务器的端口上,...

    1、服务器模型

    1.1C/S模型

    【1】C/S(客户端/服务器)模型:所有客户端都通过访问服务器来获取所需的资源

    这里写图片描述

    【2】C/S模型逻辑:服务器启动后,先创建一个或者多个监听socket,并调用bind函数将其绑定到服务器的端口上,然后调用listen函数等待客户连接。服务器稳定运行后,客户端就可以调用connect函数向服务器发起连接。由于客户连接请求时随机到达的异步事件,服务器需要使用某种I/O模型来监听这一事件,(图中使用的是I/O复用技术之一的select系统调用),当监听到连接请求后,服务器就调用accept函数接受它了,并且分配一个逻辑单元为新的连接服务。逻辑单元读取客户请求,处理该请求,然后将结果反馈给客户端

    这里写图片描述

    【3】C/S模型适合资源相对集中的场合,但是服务器是通信的中心,当访问量过大时,可能所有客户都将得到很慢的响应。

    2.1P2P模型

    【1】P2P模型不在是以服务器为中心的格局,它使得每台机器在消耗服务的同时也给别人提供服务,这样资源能充分、自由的分享。
    【2】问题:a图中,主机之间很难互相发现。所以实际的P2P模型通常还带有一个专门的发现服务器;如图b,这个发现服务器提供查找服务(甚至还可提供内容服务),使每个客户都能尽快找到自己需要的资源。

    这里写图片描述

    【3】缺点:当用户之间传输的请求过多时,网络负载加重。

    2、服务器编程框架

    服务器种类繁多,但是大体框架都一样,不同点在于逻辑处理

    这里写图片描述

    模块 单个服务器程序 服务器集群
    I/O处理单元 处理客户连接,读写网络数据 作为接入服务器,实现负载均衡
    逻辑单元 业务进程或线程 逻辑服务器
    网络存储单元 本地数据库,文件或缓存 数据库服务器
    请求队列 各单元之间的通信方式 各服务器之间的永久TCP连接

    3、I/O模型

    【1】称阻塞的文件描述符为阻塞I/O,称非阻塞的文件描述符为非阻塞I/O。

    【2】针对阻塞I/O执行的系统调用可能因为无法立即完成而被操作系统挂起,直到等待的事件发生为止。针对非阻塞I/O执行的系统调用则总是立即返回,而不管时间是否已经发生。所以,只有在事件已经发生的情况下操作非阻塞I/O,才能提高程序的效率。因此,非阻塞I/O通常要和其他I/O通知机制一起使用,比如I/O复用和SIGIO信号。

    【3】I/O复用是最常使用的I/O通知机制:应用程序通过I/O复用函数向内核注册一组事件,内核通过I/O复用函数把其中就绪的事件通知给应用程序。I/O复用函数本身是阻塞的,它能提高程序效率的原因在于它具有同时监听多个I/O事件的能力

    【4】SIGIO信号:为一个目标文件描述符指定宿主进程,那么被指定的宿主进程讲捕获到SIGIO信号。这样,当目标文件描述符上有事件发生时,SIGIO信号的信号处理函数将被触发,也就可以在该信号处理函数中对目标文件描述符执行非阻塞I/O操作

    理论上,阻塞I/O,I/O复用和信号驱动I/O都是同步I/O模型。因为三种模型中,I/O的读写操作都是在I/O事件发生之后,由应用程序来完成的。而对于异步I/O模型,用户可以直接对I/O执行读写操作,会告诉内核读写缓冲区的位置,以及I/O操作完成之后内核通知应用程序的方式。异步I/O的读写操作总是立即返回,而不论I/O是否阻塞,因为真正的读写操作已经由内核接管。

    I/O模型 读写操作和阻塞阶段
    阻塞I/O 程序阻塞与读写函数
    I/O复用 程序阻塞于I/O复用系统调用,但可同时监听多个I/O事件、对I/O本身的读写操作是非阻塞的
    SIGIO信号 信号触发读写事件就绪,用户程序执行读写操作,程序没有阻塞阶段
    异步I/O 内核执行读写操作并触发读写完成事件,程序没有阻塞阶段

    4、两种高效的事件处理模式

    4.1Reactor模式

    【1】Reactor模式:主线程(I/O处理单元)只负责监听文件描述符上是否有事件发生,有的话就立即将事件通知工作线程(逻辑单元)。除此之外,主线程不做任何其他实质性的工作;读写数据以及处理客户请求均在工作线程中完成。
    【2】使用同步I/O模型(以epoll_wait为例)实现的Reactor模式的工作流程:

    • 主线程往epoll内核事件表里面注册socket上的读事件。
    • 主线程调用epoll_wait等待socket上有数据可读。
    • 当socket上有数据可读时,epoll_wait通知主线程。主线程将socket可读事件放入请求队列。
    • 睡眠在请求队列上的某个工作线程被唤醒,它从socket上读取数据,并处理客户请求,然后往epoll内核事件表中注册该socket上的写事件。
    • 主线程调用epoll_wait等待socket可写。
    • 当socket可写时,epoll_wait通知主线程。主线程将socket上的可写事件放入请求队列。
    • 睡眠在请求队列上的某个工作线程被唤醒,它往socket上写入服务器处理客户请求的结果。

    这里写图片描述

    4.2Proactor模式

    【1】Proactor模式:讲所有I/O操作都交给主线程和内核来处理,工作线程只负责业务逻辑。
    【2】使用异步I/O模型(aio_read和aio_write为例)实现的Proactor模式的工作流程:

    • 主线程调用aio_read函数向内核注册socket上的读事件,并告诉内核用户缓冲区的位置,和缓冲区大小,以及读操作完成时如何通知应用程序。(以信号为例)
    • 主线程继续处理其他逻辑。
    • 当socket上的数据被读入用户缓冲区之后,内核将向应用程序发送一个信号,告诉应用程序数据已经可用。
    • 应用程序预先定义好的信号处理函数选择一个工作线程来处理客户请求。工作线程处理完成之后,调用aio_write函数向内核注册socket上的写事件,并告诉内核用户写缓冲区的位置和大小,以及写操作完成时如何通知应用程序。
    • 主线程继续处理其他逻辑。
    • 当用户缓冲区的数据被写入到socket之后,内核将向应用程序发送一个信号,通知应用程序写事件已经完毕。
    • 应用层序预先定义好的信号处理函数选择一个工作线程来做善后处理。

    这里写图片描述

    展开全文
  • 在文章《unix网络编程》(12)五I/O模型中提到了五I/O模型,其中前四:阻塞模型、非阻塞模型、信号驱动模型、I/O复用模型都是同步模型;还有一是异步模型。 Reactor模型 Reactor模式是处理并发I/O比较常见...

    I/O模型

    在文章《unix网络编程》(12)五种I/O模型中提到了五种I/O模型,其中前四种:阻塞模型、非阻塞模型、信号驱动模型、I/O复用模型都是同步模型;还有一种是异步模型。

    Reactor模型

    Reactor模式是处理并发I/O比较常见的一种模式,用于同步I/O,中心思想是将所有要处理的I/O事件注册到一个中心I/O多路复用器上,同时主线程/进程阻塞在多路复用器上;一旦有I/O事件到来或是准备就绪(文件描述符或socket可读、写),多路复用器返回并将事先注册的相应I/O事件分发到对应的处理器中。

    Reactor是一种事件驱动机制,和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的事件发生,Reactor将主动调用应用程序注册的接口,这些接口又称为“回调函数”。用“好莱坞原则”来形容Reactor再合适不过了:不要打电话给我们,我们会打电话通知你。

    Reactor模式与Observer模式在某些方面极为相似:当一个主体发生改变时,所有依属体都得到通知。不过,观察者模式与单个事件源关联,而反应器模式则与多个事件源关联 。

    在Reactor模式中,有5个关键的参与者:

    1. 描述符(handle): 
      由操作系统提供的资源,用于识别每一个事件,如Socket描述符、文件描述符、信号的值等。在Linux中,它用一个整数来表示。事件可以来自外部,如来自客户端的连接请求、数据等。事件也可以来自内部,如信号、定时器事件。
    2. 同步事件多路分离器(event demultiplexer): 
      事件的到来是随机的、异步的,无法预知程序何时收到一个客户连接请求或收到一个信号。所以程序要循环等待并处理事件,这就是事件循环。在事件循环中,等待事件一般使用I/O复用技术实现。在linux系统上一般是select、poll、epol_waitl等系统调用,用来等待一个或多个事件的发生。I/O框架库一般将各种I/O复用系统调用封装成统一的接口,称为事件多路分离器。调用者会被阻塞,直到分离器分离的描述符集上有事件发生。
    3. 事件处理器(event handler): 
      I/O框架库提供的事件处理器通常是由一个或多个模板函数组成的接口。这些模板函数描述了和应用程序相关的对某个事件的操作,用户需要继承它来实现自己的事件处理器,即具体事件处理器。因此,事件处理器中的回调函数一般声明为虚函数,以支持用户拓展。
    4. 具体的事件处理器(concrete event handler): 
      是事件处理器接口的实现。它实现了应用程序提供的某个服务。每个具体的事件处理器总和一个描述符相关。它使用描述符来识别事件、识别应用程序提供的服务。
    5. Reactor 管理器(reactor): 
      定义了一些接口,用于应用程序控制事件调度,以及应用程序注册、删除事件处理器和相关的描述符。它是事件处理器的调度核心。 Reactor管理器使用同步事件分离器来等待事件的发生。一旦事件发生,Reactor管理器先是分离每个事件,然后调度事件处理器,最后调用相关的模 板函数来处理这个事件。

    可以看出,是Reactor管理器并不是应用程序负责等待事件、分离事件和调度事件。Reactor并没有被具体的事件处理器调度,而是管理器调度具体的事件处理器,由事件处理器对发生的事件作出处理,这就是Hollywood原则。应用程序要做的仅仅是实现一个具体的事件处理器,然后把它注册到Reactor管理器中。接下来的工作由管理器来完成:如果有相应的事件发生,Reactor会主动调用具体的事件处理器,由事件处理器对发生的事件作出处理。

    应用场景

    场景:长途客车在路途上,有人上车有人下车,但是乘客总是希望能够在客车上得到休息。 
    传统做法:每隔一段时间(或每一个站),司机或售票员对每一个乘客询问是否下车。 
    Reactor做法:汽车是乘客访问的主体(Reactor),乘客上车后,到售票员(acceptor)处登记,之后乘客便可以休息睡觉去了,当到达乘客所要到达的目的地时(指定的事件发生,乘客到了下车地点),售票员将其唤醒即可。

    为什么使用Reactor

    网络编程为什么要用反应堆?有了I/O复用,有了epoll已经可以使服务器并发几十万连接的同时,维持高TPS了,难道这还不够吗?

    答案是,技术层面足够了,但在软件工程层面却是不够的。

    程序使用IO复用的难点在哪里呢?

    1个请求可能由多次IO处理完成,但相比传统的单线程完整处理请求生命期的方法,IO复用在人的大脑思维中并不自然,因为,程序员编程中,处理请求A的时候,假定A请求必须经过多个IO操作A1-An(两次IO间可能间隔很长时间),每经过一次IO操作,再调用IO复用时,IO复用的调用返回里,非常可能不再有A,而是返回了请求B。即请求A会经常被请求B打断,处理请求B时,又被C打断。这种思维下,编程容易出错。

    形象例子:

    本部分和下部分内容来自:高性能网络编程6–reactor反应堆与定时器管理

    传统编程方法就好像是到了银行营业厅里,每个窗口前排了长队,业务员们在窗口后一个个的解决客户们的请求。一个业务员可以尽情思考着客户A依次提出的问题,例如:

    “我要买2万XX理财产品。“ 
    “看清楚了,5万起售。” 
    “等等,查下我活期余额。” 
    “余额5万。” 
    “那就买 5万吧。” 
    业务员开始录入信息。 
    ”对了,XX理财产品年利率8%?” 
    “是预期8%,最低无利息保本。“ 
    ”早不说,拜拜,我去买余额宝。“ 
    业务员无表情的删着已经录入的信息进行事务回滚。 
    ”下一个!“

    用了IO复用则是大师业务员开始挑战极限:

    在超大营业厅里给客户们人手一个牌子,黑压压的客户们都在大厅中,有问题时举牌申请提问,大师目光敏锐点名指定某人提问,该客户迅速得到大师的答复后,要经过一段时间思考,查查自己的银袋子,咨询下LD,才能再次进行下一个提问,直到得到完整的满意答复退出大厅。例如:大师刚指导A填写转帐单的某一项,B又来申请兑换泰铢,给了B兑换单后,C又来办理定转活,然后D与F在争抢有限的圆珠笔时出现了不和谐现象,被大师叫停业务,暂时等待。

    这就是基于事件驱动的IO复用编程比起传统1线程1请求的方式来,有难度的设计点了,客户们都是上帝,既不能出错,还不能厚此薄彼。

    当没有反应堆时,我们可能的设计方法是这样的:大师把每个客户的提问都记录下来,当客户A提问时,首先查阅A之前问过什么做过什么,这叫联系上下文,然后再根据上下文和当前提问查阅有关的银行规章制度,有针对性的回答A,并把回答也记录下来。当圆满回答了A的所有问题后,删除A的所有记录。

    在程序中, 某一瞬间,服务器共有10万个并发连接,此时,一次IO复用接口的调用返回了100个活跃的连接等待处理。先根据这100个连接找出其对应的对象,这并不难,epoll的返回连接数据结构里就有这样的指针可以用。接着,循环的处理每一个连接,找出这个对象此刻的上下文状态,再使用read、write这样的网络IO获取此次的操作内容,结合上下文状态查询此时应当选择哪个业务方法处理,调用相应方法完成操作后,若请求结束,则删除对象及其上下文。

    这样,我们就陷入了面向过程编程方法之中了,在面向应用、快速响应为王的移动互联网时代,这样做早晚得把自己玩死。我们的主程序需要关注各种不同类型的请求,在不同状态下,对于不同的请求命令选择不同的业务处理方法。这会导致随着请求类型的增加,请求状态的增加,请求命令的增加,主程序复杂度快速膨胀,导致维护越来越困难,苦逼的程序员再也不敢轻易接新需求、重构。

    反应堆是解决上述软件工程问题的一种途径,它也许并不优雅,开发效率上也不是最高的,但其执行效率与面向过程的使用IO复用却几乎是等价的,所以,无论是nginx、memcached、redis等等这些高性能组件的代名词,都义无反顾的一头扎进了反应堆的怀抱中。

    反应堆模式可以在软件工程层面,将事件驱动框架分离出具体业务,将不同类型请求之间用OO的思想分离。通常,反应堆不仅使用IO复用处理网络事件驱动,还会实现定时器来处理时间事件的驱动(请求的超时处理或者定时任务的处理),就像下面的示意图: 
    avatar

    这幅图有5点意思:

    (1)处理应用时基于OO思想,不同的类型的请求处理间是分离的。例如,A类型请求是用户注册请求,B类型请求是查询用户头像,那么当我们把用户头像新增多种分辨率图片时,更改B类型请求的代码处理逻辑时,完全不涉及A类型请求代码的修改。

    (2)应用处理请求的逻辑,与事件分发框架完全分离。什么意思呢?即写应用处理时,不用去管何时调用IO复用,不用去管什么调用epoll_wait,去处理它返回的多个socket连接。应用代码中,只关心如何读取、发送socket上的数据,如何处理业务逻辑。事件分发框架有一个抽象的事件接口,所有的应用必须实现抽象的事件接口,通过这种抽象才把应用与框架进行分离。

    (3)反应堆上提供注册、移除事件方法,供应用代码使用,而分发事件方法,通常是循环的调用而已,是否提供给应用代码调用,还是由框架简单粗暴的直接循环使用,这是框架的自由。

    (4)IO多路复用也是一个抽象,它可以是具体的select,也可以是epoll,它们只必须提供采集到某一瞬间所有待监控连接中活跃的连接。

    (5)定时器也是由反应堆对象使用,它必须至少提供4个方法,包括添加、删除定时器事件,这该由应用代码调用。最近超时时间是需要的,这会被反应堆对象使用,用于确认select或者epoll_wait执行时的阻塞超时时间,防止IO的等待影响了定时事件的处理。遍历也是由反应堆框架使用,用于处理定时事件。

    Reactor的几种模式

    在web服务中,很多都涉及基本的操作:read request、decode request、process service、encod reply、send reply等。

    1. 单线程模式 
      这是最简单的单Reactor单线程模型。Reactor线程是个多面手,负责多路分离套接字,Accept新连接,并分派请求到处理器链中。该模型适用于处理器链中业务处理组件能快速完成的场景。不过这种单线程模型不能充分利用多核资源,所以实际使用的不多。
    2. 多线程模式(单Reactor) 
      该模型在事件处理器(Handler)链部分采用了多线程(线程池),也是后端程序常用的模型。
    3. 多线程模式(多个Reactor) 
      比起第二种模型,它是将Reactor分成两部分,mainReactor负责监听并accept新连接,然后将建立的socket通过多路复用器(Acceptor)分派给subReactor。subReactor负责多路分离已连接的socket,读写网络数据;业务处理功能,其交给worker线程池完成。通常,subReactor个数上可与CPU个数等同。

    Proacotr模型

    Proactor是和异步I/O相关的。

    在Reactor模式中,事件分离者等待某个事件或者可应用或个操作的状态发生(比如文件描述符可读写,或者是socket可读写),事件分离器就把这个事件传给事先注册的处理器(事件处理函数或者回调函数),由后者来做实际的读写操作。

    在Proactor模式中,事件处理者(或者代由事件分离者发起)直接发起一个异步读写操作(相当于请求),而实际的工作是由操作系统来完成的。发起时,需要提供的参数包括用于存放读到数据的缓存区,读的数据大小,或者用于存放外发数据的缓存区,以及这个请求完后的回调函数等信息。事件分离者得知了这个请求,它默默等待这个请求的完成,然后转发完成事件给相应的事件处理者或者回调。

    可以看出两者的区别:Reactor是在事件发生时就通知事先注册的事件(读写由处理函数完成);Proactor是在事件发生时进行异步I/O(读写由OS完成),待IO完成事件分离器才调度处理器来处理。

    举个例子,将有助于理解Reactor与Proactor二者的差异,以读操作为例(类操作类似)。 
    在Reactor(同步)中实现读:

    • 注册读就绪事件和相应的事件处理器
    • 事件分离器等待事件
    • 事件到来,激活分离器,分离器调用事件对应的处理器。
    • 事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。

    Proactor(异步)中的读:

    • 处理器发起异步读操作(注意:操作系统必须支持异步IO)。在这种情况下,处理器无视IO就绪事件,它关注的是完成事件。
    • 事件分离器等待操作完成事件
    • 在分离器等待过程中,操作系统利用并行的内核线程执行实际的读操作,并将结果数据存入用户自定义缓冲区,最后通知事件分离器读操作完成。
    • 事件分离器呼唤处理器。
    • 事件处理器处理用户自定义缓冲区中的数据,然后启动一个新的异步操作,并将控制权返回事件分离器。

    现行做法

    开源C++框架:ACE

    开源C++开发框架 ACE 提供了大量平台独立的底层并发支持类(线程、互斥量等)。 同时在更高一层它也提供了独立的几组C++类,用于实现Reactor及Proactor模式。 尽管它们都是平台独立的单元,但他们都提供了不同的接口。ACE Proactor在MS-Windows上无论是性能还在健壮性都更胜一筹,这主要是由于Windows提供了一系列高效的底层异步API。(这段可能过时了点吧) 不幸的是,并不是所有操作系统都为底层异步提供健壮的支持。举例来说, 许多Unix系统就有麻烦。因此, ACE Reactor可能是Unix系统上更合适的解决方案。 正因为系统底层的支持力度不一,为了在各系统上有更好的性能,开发者不得不维护独立的好几份代码: 为Windows准备的ACE Proactor以及为Unix系列提供的ACE Reactor。真正的异步模式需要操作系统级别的支持。由于事件处理者及操作系统交互的差异,为Reactor和Proactor设计一种通用统一的外部接口是非常困难的。这也是设计通行开发框架的难点所在。

    ACE是一个大型的中间件产品,代码20万行左右,过于宏大,一堆的设计模式,架构了一层又一层,使用的时候,要根据情况,看从那一层来进行使用。支持跨平台。

    设计模式 :ACE主要应用了Reactor,Proactor等;

    层次架构 :ACE底层是C风格的OS适配层,上一层基于C++的wrap类,再上一层是一些框架 (Accpetor,Connector,Reactor,Proactor等),最上一层是框架上服务;

    可移植性 :ACE支持多种平台,可移植性不存在问题,据说socket编程在linux下有不少bugs;

    事件分派处理 :ACE主要是注册handler类,当事件分派时,调用其handler的虚挂勾函数。实现 ACE_Handler/ACE_Svc_Handler/ACE_Event_handler等类的虚函数;

    涉及范围 :ACE包含了日志,IPC,线程池,共享内存,配置服务,递归锁,定时器等;

    线程调度 :ACE的Reactor是单线程调度,Proactor支持多线程调度;

    发布方式 :ACE是开源免费的,不依赖于第三方库,一般应用使用它时,以动态链接的方式发布动态库;开发难度 :基于ACE开发应用,对程序员要求比较高,要用好它,必须非常了解其框架。在其框架下开发,往往new出一个对象,不知在什么地方释放好。

    C网络库:libevent

    libevent是一个C语言写的网络库,官方主要支持的是类linux操作系统,最新的版本添加了对windows的IOCP的支持。在跨平台方面主要通过select模型来进行支持。

    设计模式 :libevent为Reactor模式;

    层次架构:livevent在不同的操作系统下,做了多路复用模型的抽象,可以选择使用不同的模型,通过事件函数提供服务;

    可移植性 :libevent主要支持linux平台,freebsd平台,其他平台下通过select模型进行支持,效率不是太高;

    事件分派处理 :libevent基于注册的事件回调函数来实现事件分发;

    涉及范围 :libevent只提供了简单的网络API的封装,线程池,内存池,递归锁等均需要自己实现;

    线程调度 :libevent的线程调度需要自己来注册不同的事件句柄;

    发布方式 :libevent为开源免费的,一般编译为静态库进行使用;

    开发难度 :基于libevent开发应用,相对容易,具体可以参考memcached这个开源的应用,里面使用了 libevent这个库。

    改进方案:模拟异步

    在改进方案中,我们将Reactor原来位于事件处理器内的read/write操作移至分离器(不妨将这个思路称为“模拟异步”),以此寻求将Reactor多路同步IO转化为模拟异步IO。

    以读操作为例子,改进过程如下:

    • 注册读就绪事件及其处理器,并为分离器提供数据缓冲区地址,需要读取数据量等信息。 
      • 分离器等待事件(如在select()上等待)
      • 事件到来,激活分离器。分离器执行一个非阻塞读操作(它有完成这个操作所需的全部信息),最后调用对应处理器。
      • 事件处理器处理用户自定义缓冲区的数据,注册新的事件(当然同样要给出数据缓冲区地址,需要读取的数据量等信息),最后将控制权返还分离器。

    如我们所见,通过对多路IO模式功能结构的改造,可将Reactor转化为Proactor模式。改造前后,模型实际完成的工作量没有增加,只不过参与者间对工作职责稍加调换。没有工作量的改变,自然不会造成性能的削弱。对如下各步骤的比较,可以证明工作量的恒定: 
    标准/典型的Reactor:

    • 步骤1:等待事件到来(Reactor负责) 
      • 步骤2:将读就绪事件分发给用户定义的处理器(Reactor负责)
      • 步骤3:读数据(用户处理器负责)
      • 步骤4:处理数据(用户处理器负责)

    改进实现的模拟Proactor:

    • 步骤1:等待事件到来(Proactor负责) 
      • 步骤2:得到读就绪事件,执行读数据(现在由Proactor负责)
      • 步骤3:将读完成事件分发给用户处理器(Proactor负责)
      • 步骤4:处理数据(用户处理器负责)

    对于不提供异步IO API的操作系统来说,这种办法可以隐藏socket API的交互细节,从而对外暴露一个完整的异步接口。借此,我们就可以进一步构建完全可移植的,平台无关的,有通用对外接口的解决方案。上述方案已经由Terabit P/L公司(http://www.terabit.com.au/)实现为TProactor。它有两个版本:C++和JAVA的。C++版本采用ACE跨平台底层类开发,为所有平台提供了通用统一的主动式异步接口。

    Boost.Asio类库

    Boost.Asio类库,其就是以Proactor这种设计模式来实现,参见:Proactor(The Boost.Asio library is based on the Proactor pattern. This design note outlines the advantages and disadvantages of this approach.),其设计文档链接:http://asio.sourceforge.net/boost_asio_0_3_7/libs/asio/doc/design/index.html

    展开全文
  • 服务器模型——C/S模型和P2P模型

    千次阅读 2017-09-03 21:26:08
    呦呦切克闹,煎饼果子来一套本篇学习两种服务器模型~~ TCP/IP协议在设计和实现上并没有客户端和服务器的概念,在通信过程中所有机器都是对等的。但由于资源都被数据提供者所垄断,所以几乎所有的网络应用程序都...

    呦呦切克闹,煎饼果子来一套大笑本篇学习两种服务器模型~~


    TCP/IP协议在设计和实现上并没有客户端和服务器的概念,在通信过程中所有机器都是对等的。但由于资源都被数据提供者所垄断,所以几乎所有的网络应用程序都很自然地采用了下图所示的C/S(客户端/服务器)模型。

    (1)C/S(客户端/服务器)模型:所有客户端都通过访问服务器来获取所需的资源。


    C/S模型的逻辑很简单。服务器启动后,首先创建一个(或多个)监听socket,并调用bind函数将其绑定到服务器感兴趣的端口上,然后调用listen函数等待客户连接。服务器稳定运行后,客户端就可以调用connect函数向服务器发起连接 了。由于客户连接请求是随机到达的异步事件,服务器需要使用某种I/O模型来监听这一事件。I/O模型有多种,下图服务器使用的是I/O复用技术之一的select系统调用。当监听到连接请求后,服务器就调用accept函数接收它,并分配一个逻辑单元为新的连接服务。逻辑单元可以是新创建的子进程、子线程或者其它。下图中,服务器给客户端分配的逻辑单元是fork系统调用创建的子进程。逻辑单元读取客户请求,处理该请求,然后将处理结果返回给客户端。客户端接收到服务器反馈的结果之后,可以继续向服务器发送请求,也可以立即主动关闭连接。如果客户端主动关闭连接,则服务器执行被动关闭连接。至此,双方的通信结束。需要注意的是,服务器在处理一个客户请求的同时还会继续监听其他客户请求,否则就变成了效率低下的串行服务器了(必须先处理完前一个客户的请求,才能继续处理下一个客户请求)。下图中,服务器同时监听多个客户请求是通过select系统调用实现的。


    C/S模型非常适合资源相对集中的场合,并且它的实现也很简单,但其缺点也很明显:服务器是通信的中心,当访问量过大时,可能所有客户都将得到很慢的响应。下面讨论的P2P模型解决了这个问题。

    (2)P2P(点对点)模型

    P2P(点对点)模型比C/S模型更符合网络通信的实际情况。它摒弃了以服务器为中心的格局,让网络上所有的主机重新回归对等的地位。


    P2P模型使得每台机器在消耗服务的同时也给别人提供服务,这样资源能够充分、自由地共享。云计算机群可以看做P2P模型的一个典范。但P2P模型的缺点也很明显:当用户之间传输的请求过多时,网络的负载将加重。

    下图中的P2P模型存在一个显著的问题,即主机之间很难互相发现。所以实际使用的P2P模型通常带有一个专门的发现服务器。这个发现服务器通常还提供查找服务(甚至还可以提供内容服务),使每个客户都能尽快地找到自己需要的资源。



    从编程角度来讲,P2P模型可以看做C/S模型的扩展:每台主机既是客户端,又是服务器。

    展开全文
  • 服务器模型

    2017-07-27 16:41:45
    服务器模型 C/S模型 P2P模型 两种模型,他们的优点和缺点、编程过程、模型图等
  • javaweb两种开发模型

    2021-04-24 20:02:53
    两种开发模型 一、C/S又称Client/Server或客户/服务器模式。服务器通常采用高性能的PC、工作站或小型机,客户端需要安装专 用的客户端软件。如QQ,微信等软件 二、B/S是Brower/Server的缩写,客户机上只要安装一个...
  • 选用的服务器是apache-activemq-5.13.0,是在本机上打开的。 将服务器在电脑上找到对应的机型数下在bin文件夹下打开activemq.bat...JMS支持两种消息模型:Point-to-Point(P2P)和Publish/Subscribe(Pub/Sub),即
  • WebRTC服务器模型

    2020-12-21 20:26:29
    两端浏览器(clientA,clientB)可以直接音视频通话,而这种情况又可以分为两种情况: P2P(点对点通信) 成功 ClientA 与ClientB 之间直接建立起数据通道 P2P 失败 需要中转服务器参与在ClientA 与ClientB 之间进行...
  • 文章目录1 消息队列(MQ)是什么1.1 实现MQ的两种方式——AMQP/JMS1.2 RabbitMQ2 五种消息模型2.1 基本消息模型2.1.1 如何避免消息的丢失2.2 work消息模型2.2.1 说明2.2.2 消息堆积越来越多?——能者多劳2.3 三种...
  • linux网络服务器模型

    2014-12-10 16:34:20
    linux网络服务器模型 Linux系统网络服务器...而且对于TCP和UDP套接字,这两种服务器的实现方式也有不同的特点。  1、TCP循环服务器  首先TCP服务器接受一个客户端的连接请求,处理连接请求,在完成这个客户端的
  • 总体上来说,服务器端可采用两种模型来实现:循环服务器模型和并发服务器模型。  循环服务器模型是指服务器端依次处理每个客户端,直到当前客户端的所有请求处理完毕,再处理下一个客户端。这类模型的优点是简单,...
  • 在网络程序里面,通常都是一个...本节开始介绍Linux下套接字编程的服务器模型选择,主要包括循环服务器模型、并发服务器模型、IO复用服务器模型等,这也是我们常见的几网络服务器模型。其中基本可以分为大类, 1
  • 线程池 epoll 总结了一两种常用的线程模型, 归纳了进程间通讯与线程同步的最佳实践,以期用简单规范的方式开发多线程程序。
  • 是我自己写的多线程的服务器 客户端发 服务器收 用两种方式 多线程 select 还有一种是链表的
  • 两本书籍已经阅读完毕,现总结高性能服务器程序框架内容,分四篇文章介绍服务器模型、I/O模型(四种I/O模型)、两种高效事件处理模式和两种高效的并发模式。 正文开始: 1.C/S模型  TCP/IP协议在设计和实现上并...
  • 在冯诺依曼体系结构下,输入输出是重要的部分。当然,在网络中,输入输出也是重要的环。通常,在网络通信的效率中...1. IO模型的分类首先IO模型分为大类,一类是同步IO,另一类是异步IO;同步IO包括四类:阻塞...
  • workerman是一个高性能的PHP socket 服务器框架,workerman基于PHP多进程以及libevent事件轮询库,PHP开发者只要实现一个接口,便可以开发出自己的网络应用,例如Rpc服务、聊天室服务器、手机游戏服务器等。...
  • 详细介绍JSP技术的两种架构模型

    千次阅读 2015-12-28 22:59:34
    本文分别介绍这两种模型,并比较它们的优缺点。 一、JSP和JavaBean模型  其工作原理是:当浏览器发出请求时,JSP接收请求并访问JavaBean.若需要访问数据库或后台服务器,则通过JavaBean连接数据库或后台服务器,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,335
精华内容 934
关键字:

两种服务器模型