很想写一篇关于Netty的剖析,但是只写Netty有些童鞋可能会一脸懵,这篇文章从浅入深,从BIO到NIO讲了个透彻,最后再讲到Netty底层原理,期间也是翻阅资料,也算是自己对相关知识的总结和复习吧~~
1、什么是I/O模型?什么是BIO NIO AIO?三者有什么区别?
2、AIO性能更好,为什么Netty不使用AIO?
3、Redis、Nginx等为什么这么快?
4、Netty相比NIO有何优势?底层如何实现?
这些问题希望看完这篇文章之后都能找到答案~~
目录
一、I/O模型
首先要理解清楚的是I/O模型是数据传输(发送和接收)的通道和方式,其底层是由操作系统决定的。I表示input输入,O表示output输出。
1.1 阻塞和非阻塞,同步和异步
阻塞和非阻塞:
由上图可以看出当应用发起IO请求的时候,会调用系统内核的IO接口,这里的主要区别就是阻塞调用会挂起应用,等待数据读取完毕后返回,而非阻塞调用不管数据是否读取完毕都会直接返回。
阻塞和非阻塞主要指的是访问IO的线程是否会阻塞(或处于等待),那么数据如何返回呢,这里就是同步和异步的区别。
同步和异步:
对于同步调用,应用层需要自己去向系统内核询问,如果数据还未读取完毕,那此时读取文件的任务还未完成,应用层根据其阻塞和非阻塞的划分,或挂起或去做其他事情(所以同步和异步并不决定其等待数据返回时的状态);如果数据已经读取完毕,那此时系统内核将数据返回给应用层,应用层即可以用取得的数据做其他相关的事情。而对于异步调用,应用层无需主动向系统内核问询,在系统内核读取完文件数据之后,会主动通知应用层数据已经读取完毕,此时应用层即可以接收系统内核返回过来的数据,再做其他事情。
1.2 BIO(同步阻塞)
Blocking I/O
同步阻塞IO,服务器实现模式为一个连接一个线程,即客户端请求一个连接就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池改善(实现多个客户端连接服务器)。
Java中对应的实现是ServerSocket和Socket。
Tomcat 7 默认采用BIO,可以根据服务器硬件调整线程池的配置进行优化。
对于Tomcat中的实现在之前的文章中有介绍和实现:点此查看
1.3 NIO(同步非阻塞)
New I/O 或者 Non-Blocking I/O
同步非阻塞IO,服务器实现模式为一个线程处理多个请求(连接),即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有 I/O 请求就进行处理。多路复用器对应到Java中的实现就是Selector选择器。
JDK1.4之后增加了对NIO的支持。对应的实现是SocketChannel和Channel。在多核服务器上,其性能要优于BIO。
Netty就是对NIO的一个封装,拥有简单便捷的API,提升了开发效率。
1.4 AIO(异步非阻塞)
AIO采用Proactor模型,基于事件驱动,异步通知调用者。读写完成后由操作系统通知应用线程去处理,一般适用于连接数较多且连接时间较长的应用。
Proactor 模式是一个消息异步通知的设计模式,Proactor 通知的不是就绪事件,而是操作完成事件,这也就是操作系统异步 IO 的主要模型。JDK1.7之后开始支持。
这样看来AIO是优于NIO的,但为什么很少使用呢?