• 关于Nio

  Java NIO即Java Non-blocking IO(Java非阻塞I/O),是Jdk1.4之后增加的一套操作I/O工具包,又被叫做Java New IO。

  • Nio要去解决的问题

  Nio要解决的问题网上的解释一大堆,诸如银行取号、餐厅点餐等等。这些列子就不再具体地重复了,实际上就是为了使用现有的资源提供更高的生产效率。

  这让我想起了以前学习政治的时候课本里的故事,资本家为了赚取更多的剩余价值往往会想方设法提高生产效率。如何提高呢?举个简单例子,一个汽车生产厂商有若干条生产线(一条生产线负责汽车制造的所有环节),每个生产线都有相同的工人数目,每个工人都负责一个生产环节,也就是说生产发动机和生产轮胎的工人数目是一样的,但是很明显生产发动机需要的时间肯定比轮胎要长很多,那么在每一条生产线上生产发动机的那个工人往往满负荷工作,而生产轮胎的工人却很闲,这样生产效率很低。因此厂家打破了这种一条生产线生产汽车所有环节的模式,改为一个汽车零部件一条生产线,那么在发动机生产线雇佣的工人数目一定多于轮胎生产线,这样每条生产线的工人都不会闲着,通过资源的合理分配最大化利用了工人的价值,提高了生产效率,赚取了剩余价值。

  而如何通过资源合理分配来提高生产效率就是nio在计算机io领域要解决的问题。

  • 同步/异步、阻塞/非阻塞

  同步和异步是针对应用程序和内核的交互而言的:

  同步指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪;异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知。

  阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作函数的实现方式:

  阻塞方式下读取或者写入函数将一直等待;非阻塞方式下,读取或者写入函数会立即返回一个状态值。

  • Reactor(反应器)模式:

  

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

     

  在Reactor模式中有如下几个角色:

  1. Reactor    

  事件分离者,其核心是Selector,负责响应IO事件,一旦发生,广播发送给相应的Handler去处理。具体为一个Selector和一个ServerSocketChannel。ServerSocketChannel注册到Selector中,获取的SelectionKey绑定一个Acceptor(也可以理解为一个Handler)。

  参考代码如下:

 import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;
import java.util.Set; /**
* 反应器模式 用于解决多用户访问并发问题
*/
public class Reactor implements Runnable {
public final Selector selector;
public final ServerSocketChannel serverSocketChannel; public Reactor(int port) throws IOException {
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
InetSocketAddress inetSocketAddress = new InetSocketAddress(InetAddress.getLocalHost(), port);
serverSocketChannel.socket().bind(inetSocketAddress);
serverSocketChannel.configureBlocking(false); // 向selector注册该channel
SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); // 利用selectionKey的attache功能绑定Acceptor 如果有事情,触发Acceptor
selectionKey.attach(new Acceptor(this));
} @Override
public void run() {
try {
while (!Thread.interrupted()) {
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectionKeys.iterator();
// Selector如果发现channel有OP_ACCEPT或READ事件发生,下列遍历就会进行。
while (it.hasNext()) {
// 来一个事件 第一次触发一个accepter线程,SocketReadHandler
SelectionKey selectionKey = it.next();
dispatch(selectionKey);
selectionKeys.clear();
}
}
} catch (IOException e) {
e.printStackTrace();
}
} /**
* 运行Acceptor或SocketReadHandler
*
* @param key
*/
void dispatch(SelectionKey key) {
Runnable r = (Runnable) (key.attachment());
if (r != null) {
r.run();
}
} }

  2. Acceptor

  也可以理解为一个Handler,这个Handler只负责创建具体处理IO请求的Handler,如果Reactor广播时SelectionKey创建一个Handler负责绑定相应的SocketChannel到Selector中。下次再次有IO事件时会调用对用的Handler去处理。

  参考代码如下:

 import java.io.IOException;
import java.nio.channels.SocketChannel; public class Acceptor implements Runnable {
private Reactor reactor; public Acceptor(Reactor reactor) {
this.reactor = reactor;
} @Override
public void run() {
try {
SocketChannel socketChannel = reactor.serverSocketChannel.accept();
if (socketChannel != null){
// 调用Handler来处理channel
new SocketReadHandler(reactor.selector, socketChannel);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

  3. Handler

  具体的事件处理者,例如ReadHandler、SendHandler,ReadHandler负责读取缓存中的数据,然后再调用一个工作处理线程去处理读取到的数据。具体为一个SocketChannel,Acceptor初始化该Handler时会将SocketChannel注册到Reactor的Selector中,同时将SelectionKey绑定该Handler,这样下次就会调用本Handler。

  参考代码如下:

 import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel; public class SocketReadHandler implements Runnable {
private SocketChannel socketChannel; public SocketReadHandler(Selector selector, SocketChannel socketChannel) throws IOException {
this.socketChannel = socketChannel;
socketChannel.configureBlocking(false); SelectionKey selectionKey = socketChannel.register(selector, 0); // 将SelectionKey绑定为本Handler 下一步有事件触发时,将调用本类的run方法。
// 参看dispatch(SelectionKey key)
selectionKey.attach(this); // 同时将SelectionKey标记为可读,以便读取。
selectionKey.interestOps(SelectionKey.OP_READ);
selector.wakeup();
} /**
* 处理读取数据
*/
@Override
public void run() {
ByteBuffer inputBuffer = ByteBuffer.allocate(1024);
inputBuffer.clear();
try {
socketChannel.read(inputBuffer);
// 激活线程池 处理这些request
// requestHandle(new Request(socket,btt));
} catch (IOException e) {
e.printStackTrace();
}
}
}

  综上所述,我们可以把上述角色的细化理解为将一份工作的工作量细化了,或者说重新分配了一下,专人做专事,这样最大化利用了每个角色的价值,提高工作效率。

NIO及Reactor模式的更多相关文章

  1. Java进阶(五)Java I/O模型从BIO到NIO和Reactor模式

    原创文章,同步发自作者个人博客,http://www.jasongj.com/java/nio_reactor/ Java I/O模型 同步 vs. 异步 同步I/O 每个请求必须逐个地被处理,一个请 ...

  2. nio的reactor模式

    转自:http://blog.csdn.net/it_man/article/details/38417761 线程状态转换图 就是非阻塞IO 采用多路分发方式举个例子吧,你服务器做一个聊天室,按照以 ...

  3. Java进阶知识点5:服务端高并发的基石 - NIO与Reactor模式以及AIO与Proactor模式

    一.背景 要提升服务器的并发处理能力,通常有两大方向的思路. 1.系统架构层面.比如负载均衡.多级缓存.单元化部署等等. 2.单节点优化层面.比如修复代码级别的性能Bug.JVM参数调优.IO优化等等 ...

  4. Java I/O模型从BIO到NIO和Reactor模式(转)

    原创文章,转载请务必将下面这段话置于文章开头处(保留超链接).本文转发自技术世界,原文链接 http://www.jasongj.com/java/nio_reactor/ Java I/O模型 同步 ...

  5. 原生JDK网络编程- NIO之Reactor模式

    “反应”器名字中”反应“的由来: “反应”即“倒置”,“控制逆转”,具体事件处理程序不调用反应器,而向反应器注册一个事件处理器,表示自己对某些事件感兴趣,有时间来了,具体事件处理程序通过事件处理器对某 ...

  6. 【死磕 NIO】— Reactor 模式就一定意味着高性能吗?

    大家好,我是大明哥,我又来了. 为什么是 Reactor 一般所有的网络服务,一般分为如下几个步骤: 读请求(read request) 读解析(read decode) 处理程序(process s ...

  7. NIO使用Reactor模式遇到的问题

    关于Reactor模式,不再多做介绍,推荐Doug Lea大神的教程:Java 可扩展的IO 本来在Reactor的构造方法中完成一系列操作是没有问题的: public class Reactor i ...

  8. 知识联结梳理 : I/O多路复用、EPOLL(SELECT/POLL)、NIO、Event-driven、Reactor模式

    为了形成一个完整清晰的认识,将概念和关系梳理出来,把坑填平. I/O多路复用 I/O多路复用主要解决传统I/O单线程阻塞的问题.它通过单线程管理多个FD,当监听的FD有状态变化的时候的,调用回调函数, ...

  9. Reactor模式和NIO(转载二)

    本文可看成是对Doug Lea Scalable IO in Java一文的翻译. 当前分布式计算 Web Services盛行天下,这些网络服务的底层都离不开对socket的操作.他们都有一个共同的 ...

随机推荐

  1. css 的一些基本操作

    日常基本使用的一些操作,持续完善中: 设置按钮圆角:border-radius:5px; 设置高度:height: 30px; 设置宽度:width: 64px; 使用span标签内容过长自动换行解决 ...

  2. linux 压缩包覆盖问题

    最近提交代码自动化构建发布的时候,出现了之前被删除的代码还是被打包发布了的问题. 流程是这样,jenkins通过定时任务获取git的提交,检测到有新提交时,就把代码拉下来通过maven进行build. ...

  3. 报错:MySQL check the manual that corresponds to your MySQL server version for the right syntax

    今天在向MySQL中插入数据时,报了标题的错误,因为我用的是session.save(object)方法,后台打印出的object和sql语句都没有问题,后来在网上查询,发现http://blog.c ...

  4. 【翻译】XV6-DRAFT as of September 3,2014 第0章 操作系统接口

    操作系统接口 操作系统的任务是让多个程序共享计算机(资源),并且提供一系列基于计算机硬件的但更有用的服务.操作系统管理并且把底层的硬件抽象出来,举例来说,一个文字处理软件(例如word)不需要关心计算 ...

  5. python学习笔记(一)

    • Python能干嘛?[1]科学计算[2]图形化开发[3]系统脚本[4]Web服务器[5]网络爬虫[6]服务器集群自动化运维 • 常用工具:easy_install.pip.ipython.Subl ...

  6. [DUBBO]Dubbo控制台查看方法

    1.dubbo控制台安装方法网上较多,例如转载http://dtbuluo.com/blog/archives/37 2.dubbo控制台查看 由于我们组是开发安装的dubbo.zookeeper等等 ...

  7. 【转载】如何用IntelliJ IDEA 14 创建Web项目

    首先要理解一个概念:在IntelliJ IDEA中"new Project"相当于eclipse中的工作空间(Workspace),而"new Module"相 ...

  8. TTTAttributedLabel 富文本小记

    - (void)setupTipsLabel:(TTTAttributedLabel *)label { UIColor *red = [UIColor mainColor]; UIColor *gr ...

  9. Nginx 和 IIS 实现动静分离

    前段时间,搞Nginx+IIS的负载均衡,想了解的朋友,可以看这篇文章:<nginx 和 IIS 实现负载均衡>,然后也就顺便研究了Nginx + IIS 实现动静分离.所以,一起总结出来 ...

  10. shell正则表达式

    正则表达式 主要用于字符串的模式分割/匹配/查找及替换操作. 正则表达式与通配符 通配符 正则与通配符的区别: 正则匹配字符串,通配符匹配文件名. 正则--->包含匹配 通配符--->完全 ...