我们的程序基本上都是对数据的IO操作以及基于CPU的运算。
成都创新互联专注为客户提供全方位的互联网综合服务,包含不限于网站制作、成都做网站、衢州网络推广、成都微信小程序、衢州网络营销、衢州企业策划、衢州品牌公关、搜索引擎seo、人物专访、企业宣传片、企业代运营等,从售前售中售后,我们都将竭诚为您服务,您的肯定,是我们最大的嘉奖;成都创新互联为所有大学生创业者提供衢州建站搭建服务,24小时服务热线:18982081108,官方网址:www.cdcxhl.com
基于Java的开发大部分是网络相关的编程,不管是基于如Tomcat般的Web容器,或是基于Netty开发的应用间的RPC服务。为了提供系统吞吐量, 降低硬件资源的开销,IO模型也在不断适应大规模、高并发需求不断演进,今天我们就来看看这个在网络上高频出现的词汇IO模型
首先我们要明确,用户程序从计算机硬件读取数据(包括文件、网络数据等),会经历数据从硬件设备中读取到系统内核后,再拷贝到用户空间的过程。在linux系统中,针对这一操作提供了5种IO模型用于优化不同场景下的IO操作。
blocking I/O发起system call recvfrom()时,进程将一直阻塞等待另一端Socket的数据到来。在该模式下,会阻塞其他连接的建立,因此一般都会通过多线程处理Socket数据的读取。
Blocking I/O优点是简单易用,对于本地I/O而言性能很高。缺点是处理网络I/O时,造成进程阻塞,以及创建线程的资源消耗。
相对于阻塞I/O的等待,非阻塞I/O隔一段时间就就需要发起system call判断数据是否就绪。如果数据就绪,就从kernel space复制到user space,操作数据; 否则,kernel会立即返回EWOULDBLOCK这个错误。
recvfrom有个参数叫flags,默认情况下阻塞。可以设置flag为非阻塞让kernel在数据未就绪时直接返回。这就是”非阻塞”主要是指数据准备阶段。
I/O Multiplexing首先向kernel发起system call,传入file descriptor和感兴趣的事件(readable、writable等)让kernel监测, 当其中一个或多个fd数据就绪,就会返回结果。程序再发起真正的I/O操作recvfrom读取数据。
第一次发起system call不会阻塞进程,kernel的数据就绪后会发送一个signal给进程。进而发起真正的IO操作。
既然说到异步IO,则前面的几种IO模型都是同步的,由上图可以看到,在数据拷贝(内核态到用户态)时,仍然是阻塞的。在异步IO中,请求连接到内核后,从数据准备到复制整个过程 都是在内核中完成,对应用户程序不会阻塞,直到请求数据完全准备好后,通过回调函数通知用户程序完成整个IO操作。
Java中提供的IO相关的API,主要是基于操作系统底层的IO的操作。在Java中的BIO、NIO、AIO属于Java对操作系统的各种IO模型的封装。当我们使用这些API时,不用关注底层IO的实现。
同步阻塞IO,服务端通过阻塞输入流来监听客户端是否有数据写入,当处理输入数据时,程序会等待内核完成处理完成并返回后才会继续执行。
上图可以看到,服务端通过ServerSocket#accept阻塞方法监听客户端的接入,然后阻塞在通过阻塞输入流等待客户端的输入,如果一直没有输入,则其他客户端都会被阻塞在此。
我们可以通过多线程来改善,每个客户端连接时,都由独立的线程来处理,虽然通过多线程可以解决客户端间的阻塞问题,但单个线程内然是阻塞模式, 并且当客户端过多时需要足够的线程来支持,比较耗费系统资源。
同步非阻塞IO,基于多路复用模型,依赖于服务器操作系统,通过一个Selector即可监听多个连接,并进行IO处理。但要注意,如果处理IO的过程较长一样会影响到其他的连接。
服务端通过Selector#select阻塞方法,监听Channel状态,一旦有Channel准备就绪,程序才会继续往下执行,因此需要不断轮询并监控Channel的状态变更。与BIO的多线程模式非常相似,只不过BIO是基于多线程技术实现,而NIO是基于操作系统底层提供的函数,效率更好且资源消耗更少。
异步非阻塞IO,在JDK1.7之后提供了异步的相关Channel,AIO提供异步功能,基于回调函数实现,同样依赖于操作系统底层的异步IO模型,异步操作的实现是在对应的 accept、connection、read、write等方法异步执行,完成后会主动调用回调函数。
其中accept、read等方法都是非阻塞的,即立即返回结果,几乎所有的异步操作都是基于回调函数实现,这种方式不管是对操作系统资源的利用以及效率上都是最佳的实现。
虽然三种IO模型的演进是为了提升系统处理IO的能力,但是开发的复杂度也同步上升:
关于阻塞、非阻塞、同步、异步这些名词的解释,可以在网上找到很多解释,但是如何能够从本质上描述其含义,正如IO与NIO中说到的阻塞与非阻塞,又是怎么体现的呢?
我们一般说说的IO模型,其实是服务端进行IO操作执行与实现的形式,程序将数据从程序写入或读写时,与硬件设备(比如硬盘、网卡)间,基于操作系统提供的系统api实现数据由用户态与内核态交互的一种形式。
同步与异步属于程序发起请求的方式;阻塞与非阻塞属于服务响应IO操作的底层实现方式。
基于上面的理解,我们看下在Java中如何实现BIO、NIO以及AIO。
BIO
Server:
serverSocket = new ServerSocket(port);
// 阻塞直到有连接
Socket clientSocket = serverSocket.accept();
// 阻塞读取数据
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
log.info(">>>>> Server接收消息:{}" , reader.readLine());
socket.shutdownInput();
log.info(">>>>> Server回复消息:{}" , message);
PrintWriter writer = new PrintWriter(socket.getOutputStream());
writer.println(message);
Client:
// 连接服务端
socket = new Socket("127.0.0.1",port);
OutputStream out = socket.getOutputStream();
out.write(message.getBytes());
socket.shutdownOutput();
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
log.info("接收Server回复:{}", reader.readLine());
NIO
省略
AIO
Server:
//
serverSocketChannel = AsynchronousServerSocketChannel.open();
//绑定端口
serverSocketChannel.bind(new InetSocketAddress(port));
//异步接收客户端连接
serverSocketChannel.accept(null, new AcceptCompletionHandler());
/**
* 处理客户端连接
* @param
*/
public class AcceptCompletionHandler implements CompletionHandler {
@Override
public void completed(AsynchronousSocketChannel result, T attachment) {
log.info(">>> 客户端接入...");
ByteBuffer byteBuffer = ByteBuffer.allocate(512);
//异步读客户端数据
result.read(byteBuffer, byteBuffer, new ReadCompletionHandler());
//接收其他的客户端连接的
serverSocketChannel.accept(null, this);
}
@Override
public void failed(Throwable exc, T attachment) {
log.error(">>> 客户端接入失败:{}", exc.getMessage());
}
}
/**
* 处理ServerChannel读取
* @param
*/
public class ReadCompletionHandler implements CompletionHandler{
@Override
public void completed(Integer result, T attachment) {
if(attachment.hasRemaining()){
// 切换成读模式
attachment.flip();
//
if( attachment instanceof ByteBuffer ){
byte[] bytes = new byte[attachment.remaining()];
((ByteBuffer)attachment).get(bytes); // 从Buffer中取数据 get
log.info("Server接收消息:{}", new String(bytes));
}
}
}
@Override
public void failed(Throwable exc, T attachment) {
log.error("Server接收消息失败:{}", exc.getMessage());
}
}
Client:
//创建异步通道实例
socketChannel = AsynchronousSocketChannel.open();
//连接服务端,异步方式
socketChannel.connect(new InetSocketAddress("127.0.0.1",port), null, new ConnetionComplateHandler());
// 消息发送
this.socketChannel.write(Charset.defaultCharset().encode(message));
/**
*
* @param
*/
public class ConnetionComplateHandler implements CompletionHandler {
@Override
public void completed(Void result, T attachment) {
log.info("Client连接服务的成功...");
}
@Override
public void failed(Throwable exc, T attachment) {
}
}
通过了解操作系统层面的IO模型可以让我们理解IO是如何实现,以及通过Java语言提供的类库实现了操作系统底层API调用的复杂性。
网页名称:还在分不清各种IO模型?
本文链接:http://www.shufengxianlan.com/qtweb/news14/225764.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联