当前位置: 首页 > 科技观察

Netty学习I-O模型与JavaNIO编程

时间:2023-03-16 13:31:14 科技观察

一、简介1)Java一共支持3种网络编程模型/IO模式:BIO、NIO、AIO2)JavaBIO:同步和阻塞(传统阻塞)、server实现方式是一个线程一个连接,即当客户端有连接请求时,服务端需要启动一个线程进行处理。如果连接什么都不做,就会造成不必要的线程开销。3)JavaNIO:同步非阻塞,服务端实现模式是一个线程处理多个请求(连接),即客户端发送的连接请求会注册到多路复用器上,多路复用器处理I/O在轮询连接时请求。4)JavaAIO(NIO.2):异步非阻塞,AIO引入异步通道的概念,采用Proactor模式,简化程序编写,只有在有效请求时才启动线程。其特点是完成后通知操作系统服务器端程序启动一个线程进行处理,一般适用于连接数多、连接时间长的应用。2.适用场景1)BIO方式适用于连接数比较少且固定的架构。这种方式对服务器资源要求比较高,并发限制在应用。在JDK1.4之前是唯一的选择,但是程序简单易懂。2)NIO方式适用于连接数较多,连接比较短(轻操作)的架构,比如聊天服务器、弹幕系统、服务器间通信等。编程比较复杂,JDK1.4开始支持。AIO方式用于连接数较多,连接比较长(操作量大)的架构,比如相册服务器,完全调用OS参与并发操作。编程比较复杂,JDK7开始支持了。3.JavaNIO编程3.1JavaNIO基本介绍JavaNIO的全称是javanon-blockingIO,指的是JDK提供的新的API。从JDK1.4开始,Java提供了一系列新的改进的输入/输出特性,统称为NIO(NewIO),是同步非阻塞的。NIO相关类放在java.nio包和子包下,重写了原java.io包中的很多类。NIO有三个核心部分:Channel(通道)、Buffer(缓冲区)、Selector(选择器)NIO是面向缓冲区或面向块的编程。数据被读入一个buffer,供其稍后处理,需要时可以在buffer中来回移动,增加了处理过程的灵活性。使用它可以提供非阻塞的高扩展性网络JavaNIO非阻塞Blocking模式,使得一个线程从某个通道发送请求或者读取数据,但是它只能获取当前可用的数据,如果当前没有数据可用,它不会得到任何东西,而不是保持线程阻塞,所以直到数据发生变化,线程才能继续做其他事情,直到可以读取为止。非阻塞写也是如此。线程请求将一些数据写入通道,但不需要等待它完全写入。线程可以同时做其他事情。3.2NIO与BIO的比较BIO以流式方式处理数据,而NIO以块式方式处理数据。块I/O的效率远高于流I/O。BIO是阻塞的,而NIO是基于字节的非阻塞BIO。Streams和字符流操作,而NIO基于Channel(通道)和Buffer(缓冲区)操作。数据总是从通道读取到缓冲区,或者从缓冲区写入通道。Selector(选择器)用于监听多个通道的事件(例如:连接请求、数据到达等),因此单个线程可以监听多个客户端通道3.3NIO三大核心原理图3.3.1Selector、Channel和Buffer的关系图中每个通道对应一个BufferSelector对应一个线程,一个线程对应多个通道(连接)。这张图体现了选择器注册了三个通道//程序切换到哪个通道是由事件决定的,Event是一个重要的概念。Selector会根据不同的事件在通道之间切换。Buffer是一个内存块。最下面是一个通过Buffer读写数据的数组。这与BIO相同。BIO要么是输入流,要么是输出流,不能是双向的,但是NIOBuffer是可以读写的,需要flip方法来切换通道。是双向的,可以返回底层操作系统的情况,比如Linux。Buffer)3.4.1基本介绍缓冲区(Buffer):缓冲区本质上是一个可以读写数据的内存块,可以理解为一个容器对象(包括数组),它提供了一组方法,方便使用内存块高效,缓冲区对象有内置的机制来跟踪和记录缓冲区的状态变化。Channel提供了从文件和网络读取数据的通道,但是读取或写入的数据必须经过Buffer,如图:3.4.2Buffer类及其子类Buffer类定义了四个buffer,所有的buffer都有属性来提供有关的信息它包含的数据元素:2)Buffer类Write的相关方法列表,而stream只能读或者写?channel可以异步读写数据?channel可以从buffer中读取数据或者向buffer中写入数据:常用的Channel类有:FileChannel、DatagramChannel、ServerSocketChannel和SocketChannel。FileChannel用于文件的数据读写,DatagramChannel用于UDP的数据读写,ServerSocketChannel和SocketChannel用于TCP的数据读写。3.6选择器(selector)3.6.1Java的NIO基本介绍,采用非阻塞IO方式。可以用一个线程处理多个客户端连接,会用到Selector(选择器)Selector可以检测多个注册的channel上是否有事件(注:多个Channel可以以事件Selector的形式注册到同一个channel),如果有事件发生,它会获取该事件,然后对每个事件进行相应的处理。只有当connection/channel真正有读写事件时,才会进行读写,大大降低了系统开销,而且不需要为每个connection创建一个线程,也不需要维护多个线程来避免多线程之间的差距3.6.2Selector图Netty的IO线程NioEventLoop聚合了Selector(选择器,也叫多路复用器),可以并发处理成百上千个客户端连接。当线程从客户端Socket通道读写数据时,如果没有可用数据,线程可以执行其他任务。线程通常将非阻塞IO的空闲时间用于对其他通道执行IO操作,因此单个线程可以管理多个输入和输出通道。由于读写操作是非阻塞的,这样可以充分提高IO线程的运行效率,避免频繁的I/O阻塞导致线程挂起。一个I/O线程可以并发处理N个客户端连接和读写操作,从根本上解决了传统的同步阻塞I/O-连接-线程模型,架构的性能、弹性扩展性和可靠性得到极大提升。很大的推动力。3.6.3Selector方法publicstaticSelectoropen():获取一个selector对象publicintselect(longtimeout):监听所有注册的通道,当可以进行IO操作时,将对应的SelectionKey加入到内部集合中返回,参数是否超时publicSetselectedKeys():从内部集合中获取所有SelectionKeyselector.select()//blockingselector.select(1000)//blocking1000sselector.wakeup();//唤醒选择器selector.selectNow();//非阻塞,立即返回select方法返回有事件的通道数。将socketChannel注册到Selector,register(Selectorsel,intops),一个selector上可以注册多个SocketChannel,注册后返回一个SelectionKey,与Selector(collection)关联,进一步获取每个SelectionKey(某个事件发生)并通过SelectionKey反向获取SocketChannel,方法channel()可以通过获取到的channel完成业务处理可以接受新的网络连接,值为16intOP_CONNECT:表示连接已经建立,值为8intOP_READ:表示读操作,值为1intOP_WRITE:表示写操作,值为43.9ServerSocketChannelServerSocketChannel在服务器端监视新的客户端Socket连接。3.10SocketChannelSocketChannel,一个网络IO通道,专门负责读写操作。NIO将缓冲区中的数据写入通道,或将通道中的数据读入缓冲区。3.11NIO和零拷贝3.11.1基本介绍在Java程序中,常用的零拷贝有mmap(内存映射)和sendFile。所谓零拷贝,就是从操作系统的角度来看,没有CPU拷贝。3.11.2传统IO模型DMA:directmemoryaccessdirectmemorycopy(不使用CPU)3.11.4mmap优化mmap通过内存映射将文件映射到内核缓冲区,同时用户空间可以共享内核空间的数据。3.11.5sendFile优化Linux2.1版本提供了sendFile函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入SocketBuffer,同时,由于它与用户模式无关,减少了上下文切换。系统调用:需要线程上下文切换,不需要进程上下文切换,再次减少数据拷贝。还有少量数据的CPU拷贝。kernelbuffer→socketbuffer拷贝的信息很少,比如长度,偏移量等,消耗很低,可以忽略。