当前位置: 首页 > 编程日记 > 正文

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;
  final ServerSocketChannel serverSocket;

Reactor(int port) throws IOException {
    selector = Selector.open();
    serverSocket = ServerSocketChannel.open();
    InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(),port);
    serverSocket.socket().bind(address);

    serverSocket.configureBlocking(false);
    //向selector注冊该channel
     SelectionKey sk =serverSocket.register(selector,SelectionKey.OP_ACCEPT);

    logger.debug("-->Start serverSocket.register!");

    //利用sk的attache功能绑定Acceptor 假设有事情。触发Acceptor
    sk.attach(new Acceptor());
    logger.debug("-->attach(new Acceptor()!");
  }


  public void run() { // normally in a new Thread
    try {
    while (!Thread.interrupted())
    {
      selector.select();
      Set selected = selector.selectedKeys();
      Iterator it = selected.iterator();
      //Selector假设发现channel有OP_ACCEPT或READ事件发生,下列遍历就会进行。
      while (it.hasNext())
        //来一个事件 第一次触发一个accepter线程
        //以后触发SocketReadHandler
        dispatch((SelectionKey)(it.next()));
        selected.clear();
      }
    }catch (IOException ex) {
        logger.debug("reactor stop!"+ex);
    }
  }

//执行Acceptor或SocketReadHandler
  void dispatch(SelectionKey k) {
    Runnable r = (Runnable)(k.attachment());
    if (r != null){
      // r.run();

}
  }

class Acceptor implements Runnable { // inner
    public void run() {
    try {
      logger.debug("-->ready for accept!");
      SocketChannel c = serverSocket.accept();
      if (c != null)
        //调用Handler来处理channel
        new SocketReadHandler(selector, c);
      }
    catch(IOException ex) {
      logger.debug("accept stop!"+ex);
    }
    }
  }
}

以上代码中巧妙使用了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;
  final SelectionKey sk;

   static final int READING = 0, SENDING = 1;
  int state = READING;

public SocketReadHandler(Selector sel, SocketChannel c)
    throws IOException {

socket = c;

socket.configureBlocking(false);
     sk = socket.register(sel, 0);

//将SelectionKey绑定为本Handler 下一步有事件触发时,将调用本类的run方法。
    //參看dispatch(SelectionKey k)
    sk.attach(this);

    //同一时候将SelectionKey标记为可读,以便读取。


    sk.interestOps(SelectionKey.OP_READ);
    sel.wakeup();
  }

public void run() {
    try{
    // test.read(socket,input);
      readRequest() ;
    }catch(Exception ex){
    logger.debug("readRequest error"+ex);
    }
  }


/**
* 处理读取data
* @param key
* @throws Exception
*/
private void readRequest() throws Exception {

ByteBuffer input = ByteBuffer.allocate(1024);
  input.clear();
  try{

int bytesRead = socket.read(input);

......

//激活线程池 处理这些request
    requestHandle(new Request(socket,btt));

.....


  }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的交互细节,从而对外暴露一个完整的异步接口。

借此,我们就能够进一步构建全然可移植的,平台无关的。有通用对外接口的解决方式。




Scalable IO in Java原文

NIO原理与应用

用NIO开发一个高性能聊天系统

Socket打造高性能server

很多其它NIO专题系列讨论....

server后端性能大比拼

事件驱动编程

并发模型

Rx (Reactive Extensions)介绍

Reactive编程

EDA

2002年大神住址:http://www.jdon.com/concurrent/reactor.htm

版权声明:本文博主原创文章,博客,未经同意不得转载。

转载于:https://www.cnblogs.com/bhlsheji/p/4849093.html

相关文章:

c语言随机数生成0 99函数,C语言生成随机数的函数、延时函数

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼下面C语言代码使用了生成随机数的函数、延时函数。请大家仔细观察其显示效果。从以下代码,我们可以得出一个重要的结论:当上述两类函数被放入循环时,应作出一定修改。同时还应关注其参数的定义位…

2022-2028年中国乙酸钴行业发展现状调研及市场前景规划报告

【报告类型】产业研究 【报告价格】4500起 【出版时间】即时更新(交付时间约3个工作日) 【发布机构】智研瞻产业研究院 【报告格式】PDF版 本报告介绍了中国乙酸钴行业市场行业相关概述、中国乙酸钴行业市场行业运行环境、分析了中国乙酸钴行业市场…

MS UI Automation Introduction

MS UI Automation Introduction 2014-09-17 MS UI Automation是什么 UIA架构 UI自动化模型 UI自动化树概述 UI自动化控件模式概述 UI 自动化属性概述 UI 自动化事件概述 示例 使用UISpy工具 UI自动化提供者 常见问题分析解决 控件无法识别 Timing issue 本地化问题 自动化技术和…

spring+mybatis事务的readonly属性无效

在Spring配置事务中设置的read-only"true"不起作用,仍可以执行写操作;但是其他的正常。查看了一下DataSourceTransactionManager这个类的doBegin方法中没有判断read-only。HibernateTransactionManager.doBegain方法中则有一判断 if(!definiti…

ue5新手零基础学习教程 Unreal Engine 5 Beginner Tutorial - UE5 Starter Course

ue5新手零基础学习教程 Unreal Engine 5 Beginner Tutorial - UE5 Starter Course! 教程大小解压后:4.96G 语言:英语中英文字幕(机译)时长:4小时56分 1920X1080 mp4 虚幻引擎5新手教程!在这个教程中&…

C语言科学计算器思路,大神教你如何用C语言实现科学计算器

i;*leni;return temp;}/*功能:翻译操作数* 如果运算符非法,则返回0,合法则返回非零标志*/int translateopt(char *p,int *len){char fu[NUM_OPT][LEN_OPT]{"","-","*","/","^","(&qu…

BZOJ4292 : [PA2015]Równanie

注意到f(n)不会超过1459&#xff0c;于是暴力枚举f(n)&#xff0c;检验nk*f(n)是否合法即可。 #include<cstdio> long long k,a,b,t;int i,j,ans; int main(){scanf("%lld%lld%lld",&k,&a,&b);for(i1;i<1459&&k<b/i;i){tk*i;if(a<…

Docker入门六部曲——Stack

原文链接&#xff1a;http://www.dubby.cn/detail.html?id8739 准备知识 安装Docker&#xff08;版本最低1.13&#xff09;。阅读完Docker入门六部曲——Swarm&#xff0c;并且完成其中介绍的内容。拷贝一份docker-compose.yml。确保你的虚拟机都是可用的&#xff0c;使用do…

SVD神秘值分解

SVD分解 SVD分解是LSA的数学基础&#xff0c;本文是我的LSA学习笔记的一部分&#xff0c;之所以单独拿出来&#xff0c;是由于SVD能够说是LSA的基础&#xff0c;要理解LSA必须了解SVD&#xff0c;因此将LSA笔记的SVD一节单独作为一篇文章。本节讨论SVD分解相关数学问题&#xf…

ebook download websites (to be continue...)

http://free-ebook-collection.blogspot.com/转载于:https://www.cnblogs.com/jerryhong/archive/2008/10/24/1318469.html

Blender模块化建筑环境地形场景制作视频教程 Creating modular environments

Blender模块化建筑环境地形场景制作视频教程 Creating modular environments Blender模块化建筑环境地形场景制作视频教程 Creating modular environments 流派:电子学习| MP4 |视频:h264&#xff0c;1280720 |音频:AAC&#xff0c;44.1 KHz 语言&#xff1a;英语中英文字幕&…

文件系统演示C语言,基于C语言的简单文件系统的实现

1 题目介绍通过具体的文件存储空间的管理、文件物理结构、目录结构和文件操作的实现&#xff0c;加深对文件系统内部的数据结构、功能以及实现过程的理解。在内存中开辟一个虚拟磁盘空间作为文件存储分区&#xff0c;在其上实现一个简单的基于多级目录的单用户单任务系统中的文…

深度学习 vs 机器学习 vs 模式识别

http://www.csdn.net/article/2015-03-24/2824301 【编者按】本文来自CMU的博士&#xff0c;MIT的博士后&#xff0c;vision.ai的联合创始人Tomasz Malisiewicz的个人博客文章&#xff0c;阅读本文&#xff0c;你可以更好的理解计算机视觉是怎么一回事&#xff0c;同时对机器学…

2022-2028年中国乙丙橡胶行业市场全景调查及投资潜力研究报告

【报告类型】产业研究 【报告价格】4500起 【出版时间】即时更新&#xff08;交付时间约3个工作日&#xff09; 【发布机构】智研瞻产业研究院 【报告格式】PDF版 本报告介绍了中国乙丙橡胶行业市场行业相关概述、中国乙丙橡胶行业市场行业运行环境、分析了中国乙丙橡胶行…

ubuntu 下利用ndiswrapper安装无线网卡驱动

本文转载自 http://kangxincai.is-programmer.com/posts/10488.html 首先 安装 ndiswrapperubuntu下也就是 ndisgtk (用于安装无线网卡驱动)sudo apt-get install ndisgtk安装好了之后&#xff0c;找到你的无线网卡在windows下的驱动文件(含有.inf的目录)&#xff08;可以从网…

TSP问题——动态规划

Traveling Salesman Problem Description&#xff1a; Time Limit: 4sec Memory Limit:256MB 有编号1到N的N个城市&#xff0c;问从1号城市出发&#xff0c;遍历完所有的城市并最后停留在N号城市的最短路径长度。 Input&#xff1a;…

Blender从头到尾创建一辆宝马轿车视频教程

Blender从头到尾创建一辆宝马轿车视频教程 Blender: Create Realistic BMW 507 From Start to Finish 流派:电子学习| MP4 |视频:h264&#xff0c;1280720 |音频:AAC&#xff0c;44.1 KHz 语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&#xff09;|大…

OC学习笔记之Foundation框架NSNumber、NSValue和NSDate(转)

一、NSNumber OC数组类NSArray&#xff0c;它只能存放 OC的对象&#xff0c;对于基本的数据类型确无能为力&#xff0c;但是实际编程中经常要把基本的数据如int、float&#xff0c;结构体存放的OC数组中&#xff0c;怎么办&#xff1f;这里的 NSNumber就有用了&#xff0c;它能…

android读取xml 字符串,Android 读取本地Xml文件,并转换成String

问题不是解析本地 xml 文件&#xff0c;而是要将 xml 文件中的所有内容(包含格式&#xff0c;标签等)&#xff0c;直接转换成 String。与前端H5页面交互时&#xff0c; iOS 在请求远程 xml 文件耗时太长(有时需要4~5s)&#xff0c;所以采用本地下载&#xff0c;然后传给前端的方…

Docker入门六部曲——容器

原文链接&#xff1a;http://www.dubby.cn/detail.html?id8734 准备 已经安装好Docker 1.13或者以上的版本。读完的上一篇文章&#xff08;基本引导&#xff09;。简单的测试一下你的本地环境是否已经OK了&#xff1a;docker run hello-world。 介绍 让我们开始构建一个Doc…

window.name实现的跨域数据传输

2019独角兽企业重金招聘Python工程师标准>>> 这篇文章是对 JavaScript跨域总结与解决办法 的补充。 有三个页面&#xff1a; a.com/app.html&#xff1a;应用页面。a.com/proxy.html&#xff1a;代理文件&#xff0c;一般是一个没有任何内容的html文件&#xff0c;需…

ajax frameworks(转贴)

Thinking in AJAX(三) —— AJAX框架汇总 引 此文原出于AJAX Patterns网站的一篇《Ajax Frameworks》的wiki文章&#xff0c;很早前我就注意到&#xff0c;后来在国内也有人翻译了&#xff0c;不过最近发现此wiki还是在不断添加维护中&#xff0c;截止此文发布前&#xff0c;作…

Maya角色面部表情动画制作视频教程 Maya: Facial Rigging

Maya角色面部表情动画制作视频教程 Maya: Facial Rigging Maya角色面部表情动画制作视频教程 Maya: Facial Rigging Maya角色面部表情动画制作视频教程 Maya: Facial Rigging MP4 |视频:h264&#xff0c;1280x720 |音频:AAC&#xff0c;44.1 KHz&#xff0c;2 Ch 语言&#x…

(一三〇)UITextField的光标操作扩展

简介 在iOS开发中&#xff0c;有时候需要完全自主的定义键盘&#xff0c;用于完整的单词输入&#xff0c;例如计算机应用中&#xff0c;需要一次性的输入sin(&#xff0c;在移动光标时要完整的跳过sin(&#xff0c;在删除时也要完整的删除&#xff0c;这就需要对光标的位置进行…

android 多个占位符,Android多语言支持:由于占位符计数不同导致的字符串格式问题...

我正在制作一个法语Android应用程序,我正在努力支持英语.我使用“占位符”来格式化我的字符串,因此我可以将它们调整为男性和女性用户.例如,我的s​​trings.xml文件中的这个字符串&#xff1a;Les %1$s sont compliqu%2$ss...将成为“Les hommessontcompliqus”(“男人很复杂”…

Docker入门六部曲——Swarm

原文链接&#xff1a;http://www.dubby.cn/detail.html?id8738 准备工作 安装Docker&#xff08;版本最低1.13&#xff09;。安装好Docker Compose&#xff0c;上一篇文章介绍过的。安装好Docker Machine&#xff0c;上一篇文章也提到了&#xff0c;Mac和Windows已经预先安装…

Ubuntu 查看磁盘空间大小命令转

df -hDf命令是linux系统以磁盘分区为单位查看文件系统&#xff0c;可以加上参数查看磁盘剩余空间信息&#xff0c;命令格式&#xff1a;df -hl显示格式为&#xff1a; 文件系统 容量 已用 可用 已用% 挂载点 Filesystem Size Used Avail Use% Moun…

MSSQLid清零

truncate table [cellphone2016].[dbo].[tp_phone_9]转载于:https://www.cnblogs.com/wangchuang/p/5259615.html

Blender 3D插图插画设计视频教程 Fantastic 3D illustration with Blender

Blender 3D插图插画设计视频教程 Fantastic 3D illustration with Blender Blender 3D插图插画设计视频教程 Fantastic 3D illustration with Blender Blender 3D插图插画设计视频教程 Fantastic 3D illustration with Blender Brellias |时长:1h 30m |视频:H264 1920x1080 |音…

Linux搜索文件&搜索文件名&替换文件内容

locate是Linux系统提供的一种快速检索全局文件的系统命令,它并不是真的去检索所以系统目录,而是检索一个数据库文件locatedb(Ubuntu系置/var/cache/locate/locatedb),该数据库文件包含了系统所有文件的路径索引信息,所以查找速度很快。time结尾的选项,其单位为天,min结尾的选项其单位为分钟,这些选项的值都为一个正负整数, 如+7,表示,7天以前被访问过的文件,-7表示7天以内被访问过的文件,7表示恰好7天前被访问的文件。:快速返回某个指定命令的位置信息。