Nio得知3——该示范基地:多路复用器模式
Reactor模式和NIO
本文可以看作是Doug Lea Scalable IO in Java一文的翻译。
当前分布式计算 Web Services盛行天下,这些网络服务的底层都离不开对socket的操作。他们都有一个共同的结构:
1. Read request
2. Decode request
3. Process service
4. Encode reply
5. Send reply
经典的网络服务的设计例如以下图。在每一个线程中完毕对数据的处理:
但这样的模式在用户负载添加时,性能将下降非常的快。我们须要又一次寻找一个新的方案,保持数据处理的流畅。非常显然。事件触发机制是最好的解决的方法,当有事件发生时,会触动handler,然后開始数据的处理。
Reactor模式类似于AWT中的Event处理:
Reactor模式參与者
1.Reactor 负责响应IO事件。一旦发生。广播发送给对应的Handler去处理,这类似于AWT的thread
2.Handler 是负责非阻塞行为。类似于AWT ActionListeners;同一时候负责将handlers与event事件绑定,类似于AWT addActionListener
如图:
Java的NIO为reactor模式提供了实现的基础机制,它的Selector当发现某个channel有数据时,会通过SlectorKey来告知我们。在此我们实现事件和handler的绑定。
我们来看看Reactor模式代码:
public class Reactor implements Runnable{ final Selector selector; Reactor(int port) throws IOException { public void run() { // normally in a new Thread //执行Acceptor或SocketReadHandler } class Acceptor implements Runnable { // inner |
以上代码中巧妙使用了SocketChannel的attach功能,将Hanlder和可能会发生事件的channel链接在一起。当发生事件时,能够马上触发对应链接的Handler。
再看看Handler代码:
public class SocketReadHandler implements Runnable {
public static Logger logger = Logger.getLogger(SocketReadHandler.class); private Test test=new Test(); final SocketChannel socket; public SocketReadHandler(Selector sel, SocketChannel c) socket = c; socket.configureBlocking(false); //将SelectionKey绑定为本Handler 下一步有事件触发时,将调用本类的run方法。 sk.interestOps(SelectionKey.OP_READ); public void run() { /** ByteBuffer input = ByteBuffer.allocate(1024); int bytesRead = socket.read(input); ...... //激活线程池 处理这些request ..... }catch(Exception e) { |
注意在Handler里面又运行了一次attach,这样,覆盖前面的Acceptor,下次该Handler又有READ事件发生时,将直接触发Handler.从而開始了数据的读 处理 写 发出等流程处理。
将数据读出后,能够将这些数据处理线程做成一个线程池,这样,数据读出后,马上扔到线程池中。这样加速处理速度:
更进一步,我们能够使用多个Selector分别处理连接和读事件。
一个高性能的Java网络服务机制就要形成,激动人心的集群并行计算即将实现。
两种I/O多路复用模式:Reactor和Proactor
一般地,I/O多路复用机制都依赖于一个事件多路分离器(Event Demultiplexer)。分离器对象可将来自事件源的I/O事件分离出来,并分发到相应的read/write事件处理器(Event Handler)。开发者预先注冊须要处理的事件及其事件处理器(或回调函数);事件分离器负责将请求事件传递给事件处理器。两个与事件分离器有关的模式是Reactor和Proactor。
Reactor模式採用同步IO。而Proactor採用异步IO。
在Reactor中,事件分离器负责等待文件描写叙述符或socket为读写操作准备就绪,然后将就绪事件传递给相应的处理器,最后由处理器负责完毕实际的读写工作。
而在Proactor模式中,处理器--或者兼任处理器的事件分离器,仅仅负责发起异步读写操作。IO操作本身由操作系统来完毕。
传递给操作系统的參数须要包含用户定义的数据缓冲区地址和数据大小。操作系统才干从中得到写出操作所需数据,或写入从socket读到的数据。事件分离器捕获IO操作完毕事件,然后将事件传递给相应处理器。比方,在windows上,处理器发起一个异步IO操作。再由事件分离器等待IOCompletion事件。典型的异步模式实现,都建立在操作系统支持异步API的基础之上,我们将这样的实现称为“系统级”异步或“真”异步。由于应用程序全然依赖操作系统运行真正的IO工作。
举个样例。将有助于理解Reactor与Proactor二者的差异,以读操作为例(类操作类似)。
在Reactor中实现读:
- 注冊读就绪事件和对应的事件处理器
- 事件分离器等待事件
- 事件到来,激活分离器,分离器调用事件相应的处理器。
- 事件处理器完毕实际的读操作,处理读到的数据,注冊新的事件,然后返还控制权。
与例如以下Proactor(真异步)中的读过程比較:
- 处理器发起异步读操作(注意:操作系统必须支持异步IO)。在这样的情况下,处理器无视IO就绪事件,它关注的是完毕事件。
- 事件分离器等待操作完毕事件
- 在分离器等待过程中。操作系统利用并行的内核线程运行实际的读操作。并将结果数据存入用户自己定义缓冲区,最后通知事件分离器读操作完毕。
- 事件分离器呼唤处理器。
- 事件处理器处理用户自己定义缓冲区中的数据,然后启动一个新的异步操作,并将控制权返回事件分离器。
实践现状
由Douglas Schmidt等人开发的开源C++开发框架ACE,提供了大量与平台无关,支持并发的底层类(线程,相互排斥量等),且在高抽象层次上,提供了两组不同的类--ACE Reactor和ACE Proactor的实现。
只是。尽管二者都与平台无关,提供的接口却各异。
ACE Proactor在windows平台上具有更为优异的性能表现。由于windows在操作系统提供了高效的异步API支持(见http://msdn2.microsoft.com/en-us/library/aa365198.aspx)。
然而,并不是全部的操作系统都在系统级大力支持异步。像非常多Unix系统就没做到。因此,在Unix上,选择ACE Reactor解决方式可能更好。但这样一来,为了获得最好的性能,网络应用的开发者必须为不同的操作系统维护多份代码:windows上以ACE Proactor为基础。而Unix系统上则採用ACE Reactor解决方式。
改进方案
在这部分,我们将尝试应对为Proactor和Reactor模式建立可移植框架的挑战。在改进方案中。我们将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的交互细节,从而对外暴露一个完整的异步接口。
借此,我们就能够进一步构建全然可移植的,平台无关的。有通用对外接口的解决方式。
事件驱动编程
Rx (Reactive Extensions)介绍
Reactive编程
EDA
2002年大神住址:http://www.jdon.com/concurrent/reactor.htm
版权声明:本文博主原创文章,博客,未经同意不得转载。
Nio得知3——该示范基地:多路复用器模式的更多相关文章
- 从Jetty、Tomcat和Mina中提炼NIO构架网络服务器的经典模式(一)
本文转载自 http://blog.csdn.net/cutesource/article/details/6192016 如何正确使用NIO来构架网络服务器一直是最近思考的一个问题,于是乎分析了一下 ...
- 从Jetty、Tomcat和Mina中提炼NIO构架网络服务器的经典模式
如何正确使用NIO来构架网络服务器一直是最近思考的一个问题,于是乎分析了一下Jetty.Tomcat和Mina有关NIO的源码,发现大伙都基于类似的方式,我感觉这应该算是NIO构架网络服务器的经典模式 ...
- 从Jetty、Tomcat和Mina中提炼NIO构架网络服务器的经典模式(三)
转载 http://blog.csdn.net/cutesource/article/details/6192163 最后我们再看看NIO方面最著名的框架Mina,抛开Mina有关session和处理 ...
- 从Jetty、Tomcat和Mina中提炼NIO构架网络服务器的经典模式(二)
本文转载自 http://blog.csdn.net/cutesource/article/details/6192145 下面再来看看Tomcat是如何使用NIO来构架Connector这块的. 先 ...
- Tomcat Connector(BIO, NIO, APR)三种运行模式(转)
Tomcat支持三种接收请求的处理方式:BIO.NIO.APR . BIO 阻塞式I/O操作即使用的是传统 I/O操作,Tomcat7以下版本默认情况下是以BIO模式运行的,由于每个请求都要创建一个线 ...
- Java进阶知识点5:服务端高并发的基石 - NIO与Reactor模式以及AIO与Proactor模式
一.背景 要提升服务器的并发处理能力,通常有两大方向的思路. 1.系统架构层面.比如负载均衡.多级缓存.单元化部署等等. 2.单节点优化层面.比如修复代码级别的性能Bug.JVM参数调优.IO优化等等 ...
- 【Tomcat】Tomcat Connector的三种运行模式【bio、nio、apr】
Tomcat Connector(Tomcat连接器)有bio.nio.apr三种运行模式 bio bio(blocking I/O,阻塞式I/O操作),表示Tomcat使用的是传统的Java I/O ...
- eventloop & actor模式 & Java线程模型演进 & Netty线程模型 总结
eventloop的基本概念可以参考:http://www.ruanyifeng.com/blog/2013/10/event_loop.html Eventloop指的是独立于主线程的一条线程,专门 ...
- Netty5序章之BIO NIO AIO演变
Netty5序章之BIO NIO AIO演变 Netty是一个提供异步事件驱动的网络应用框架,用以快速开发高性能.高可靠的网络服务器和客户端程序.Netty简化了网络程序的开发,是很多框架和公司都在使 ...
随机推荐
- HDU 1420 Prepared for New Acmer【中国剩余定理】
/* 解决问题的思路:中国剩余定理,还要注意的是数据的类型,要使用__int64位 解决人:lingnichong 解决时间:2014-08-30 06:56:35 :简单题 */ Prepared ...
- Android自己定义组件系列【5】——高级实践(1)
在接下来的几篇文章将任老师的博文<您可以下拉PinnedHeaderExpandableListView实现>骤来具体实现.来学习一下大神的代码并记录一下. 原文出处:http://blo ...
- 使用ZeroMQ(clrzmq)实现异步通信
ZeroMQ是对Socket的封装,通过组合多种类型的结点可以实现复杂的网络通信模式.而且ZeroMQ设计简单,可以有多种平台实现,对于跨平台项目是一个福音. clrzmq是ZeroMQ的C#语言的实 ...
- Enum变量值的Discretion
有些时候,某个方法的返回值是个枚举类型,比如描述登录结果: public enum LoginResult { Success, WrongPassword, } 当前段UI获取到登陆方法的返回结果时 ...
- Binomial Coeffcients 历届山东省省赛题
Binomial Coeffcients Time Limit: 1000MS Memory limit: 65536K 题目描述 输入 输出 示例输入 3 1 1 10 2 954 72 ...
- iOS缓存类的设计
使用执行速度缓存的程序可以大大提高程序,设计一个简单的缓存类并不需要太复杂的逻辑. 只需要一个简单的3接口. 存款对象 以一个对象 删除对象 阅读对象 watermark/2/text/aHR0cDo ...
- WEB安全实战(一)SQL盲注
前言 好长时间没有写过东西了,不是不想写,仅仅只是是一直静不下心来写点东西.当然,拖了这么长的时间,也总该写点什么的.近期刚刚上手安全方面的东西,作为一个菜鸟,也本着学习的目的,就谈谈近期接触到的安全 ...
- 【Unity技能】做一个简单的NPC
1. 写在前面 前几天我看到cgcookie一个教程.学习了下怎么依据已有人物模型制作一个仿版的NPC人物.感觉挺好玩的,整理一下放到博客里! 先看一下教程里面的终于效果. 是不是非常像个幽灵~ 以下 ...
- Ural 1309 Dispute (递归)
意甲冠军: 给你一个数列: f(0) = 0 f(n) = g(n,f(n-1)) g(x,y) = ((y-1)*x^5+x^3-xy+3x+7y)%9973 让你求f(n) n <= 1e ...
- JVM内存结构、垃圾回收那点事(转)
翻看电脑的文件夹,无意看到了9月份在公司做的一次分享,浏览了一下"婆婆特",发现自己在ppt上的写的引导性问题自己也不能确切的回答出来,哎,知识这东西,平时不常用的没些日子就生疏了 ...