NIO及Reactor模式
关于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模式的更多相关文章
- Java进阶(五)Java I/O模型从BIO到NIO和Reactor模式
原创文章,同步发自作者个人博客,http://www.jasongj.com/java/nio_reactor/ Java I/O模型 同步 vs. 异步 同步I/O 每个请求必须逐个地被处理,一个请 ...
- nio的reactor模式
转自:http://blog.csdn.net/it_man/article/details/38417761 线程状态转换图 就是非阻塞IO 采用多路分发方式举个例子吧,你服务器做一个聊天室,按照以 ...
- Java进阶知识点5:服务端高并发的基石 - NIO与Reactor模式以及AIO与Proactor模式
一.背景 要提升服务器的并发处理能力,通常有两大方向的思路. 1.系统架构层面.比如负载均衡.多级缓存.单元化部署等等. 2.单节点优化层面.比如修复代码级别的性能Bug.JVM参数调优.IO优化等等 ...
- Java I/O模型从BIO到NIO和Reactor模式(转)
原创文章,转载请务必将下面这段话置于文章开头处(保留超链接).本文转发自技术世界,原文链接 http://www.jasongj.com/java/nio_reactor/ Java I/O模型 同步 ...
- 原生JDK网络编程- NIO之Reactor模式
“反应”器名字中”反应“的由来: “反应”即“倒置”,“控制逆转”,具体事件处理程序不调用反应器,而向反应器注册一个事件处理器,表示自己对某些事件感兴趣,有时间来了,具体事件处理程序通过事件处理器对某 ...
- 【死磕 NIO】— Reactor 模式就一定意味着高性能吗?
大家好,我是大明哥,我又来了. 为什么是 Reactor 一般所有的网络服务,一般分为如下几个步骤: 读请求(read request) 读解析(read decode) 处理程序(process s ...
- NIO使用Reactor模式遇到的问题
关于Reactor模式,不再多做介绍,推荐Doug Lea大神的教程:Java 可扩展的IO 本来在Reactor的构造方法中完成一系列操作是没有问题的: public class Reactor i ...
- 知识联结梳理 : I/O多路复用、EPOLL(SELECT/POLL)、NIO、Event-driven、Reactor模式
为了形成一个完整清晰的认识,将概念和关系梳理出来,把坑填平. I/O多路复用 I/O多路复用主要解决传统I/O单线程阻塞的问题.它通过单线程管理多个FD,当监听的FD有状态变化的时候的,调用回调函数, ...
- Reactor模式和NIO(转载二)
本文可看成是对Doug Lea Scalable IO in Java一文的翻译. 当前分布式计算 Web Services盛行天下,这些网络服务的底层都离不开对socket的操作.他们都有一个共同的 ...
随机推荐
- jq封装淘宝图片轮播插件
<!DOCTYPE html><head><meta http-equiv="Content-Type" content="text/htm ...
- java多线程通信 例子
package com.cl.www.thread; public class NumberHolder { private Integer number = 0; // 增加number publi ...
- ROS中DDNS的使用
一.通过tool fetch更新ddns,关于此命令的使用,参考 tool fetch Scripts中添加脚本/tool fetch url="http://www.51kwl.com/? ...
- SpringMVC org.xml.sax.SAXParseException: cvc-complex-type.2.4.c 报错处理方式
使用SpringMVC的过程中需要访问静态文件,即在spring-servlet.xml文件中添加了 <mvc:default-servlet-handler /> 标签,以开启Sprin ...
- table表框去掉相邻的间隔
HTML <table> <tr> <th>名称</th> <th>描述</th> </tr> <tr> ...
- sql 取汉字首字母
)) ) --用于加密 --WITH ENCRYPTION as begin declare @intLen int ) ) set @intLen = len(@str) set @strRet = ...
- List Map Set 的用法和区别
参考 http://blog.csdn.net/vstar283551454/article/details/8682655
- VirtualBox安装MS-DOS6.22(图文教程)
最近在读一些计算机发展史之类的文献,总感觉的纸上得来终觉浅,所以打算自己装一个MS-DOS,体验一下远古的操作系统!可以google到MS-DOS的安装文件: 为什么要分成三个呢?可以看到,每个大小都 ...
- linux 基础命令与文件管理
Linux终端介绍 Shell提示符 Bash Shell基本语法 基本命令的使用:ls.pwd.cd 查看系统和BIOS硬件时间 Linux如何获得帮助 Linux关机命令:shutdow.in ...
- Java网络编程--简单聊天程序
背景 毕业设计前的练手,学校小比赛中的一个题目. 开发环境 Java(eclipse)+Mysql 简介 使用Java+Mysql开发以个简单的聊天工具,在本次项目中实现了: 1. 用户登录(客户端至 ...