网络IO传输模式和编解码方案对系统的性能影响至关重要, 作为HTTP Server, 为什么Nginx 的网络IO性能很高, 而Tomcat 之类的Web Server 网络IO 性能相对较低 ?
系统选择的网络IO模型不同, Nginx使用的poll/epoll属于 多路复用型网络模型;
Tomcat 6 之前的版本都是用的阻塞式IO模型 (6版本之后支持 NIO模式了,网络IO有所提升) , 所以导致网络IO差距比较大
1. 都有哪些常见的网络IO类型可以选择? 各有什么特点?
* blocking IO :
特点是在IO执行的两个阶段(等待数据和拷贝数据两个阶段)都被block了, 性能比较低下;

* nonblocking IO (NIO):
用户进程其实是需要不断的主动询问kernel数据准备好了没有,性能相对阻塞IO较高;
non-blocking IO在执行recvfrom这个系统调用的时候,如果kernel的数据没有准备好,这时候不会block进程。
但是当kernel中数据准备好的时候,recvfrom会将数据从kernel拷贝到用户内存中,这个时候进程是被block了,在这段时间内进程是被block的
“线程池”和“连接池”技术也只是在一定程度上缓解了频繁调用IO接口带来的资源占用。
而且,所谓“池”终有其上限,当请求大大超过上限时,“池”构成的系统对外界的响应并不比没有池的时候效果好多少。
所以使用“池”必须考虑其面临的响应规模,并根据响应规模调整“池”的大小
* IO multiplexing
本原理就是select/epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程;
在多路复用模型中,对于每一个socket,一般都设置成为non-blocking,但是,整个用户的process其实是一直被block的
* asynchronous IO
异步IO是真正非阻塞的,它不会对请求进程产生任何的阻塞
用户进程发起read操作之后,立刻就可以开始去做其它的事。从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了
总结一下以上几种模型的特征:
2. Java NIO模型的实现
目前有很多JAVA 开源软件使用Java NIO 模型(包括Netty), 比如 新版本的Tomcat, MapReduce, RocketMQ等
JAVA NIO中有几个比较关键的概念:Channel(通道),Buffer(缓冲区),Selector(选择器):
Channel:
Channel 用于在字节缓冲区和位于通道另一侧的实体(通常是一个文件或套接字)之间有效地传输数据;
Channel和传统IO中的Stream很相似。但是有很大的区别,主要区别为:通道是双向的,通过一个Channel既可以进行读,也可以进行写;而Stream只能进行单向操作,通过一个Stream只能进行读或者写;
以下是常用的几种通道:
- FileChannel—用于从文件读或者向文件写入数据
- SocketChanel —-用于以TCP来向网络连接的两端读写数据
- ServerSocketChannel —-用于服务端, 监听客户端发起的TCP连接,并为每个TCP连接创建一个新的SocketChannel来进行数据读写
- DatagramChannel—-用于以UDP协议来向网络连接的两端读写数据
Buffer(缓冲区):
缓冲区,实际上是一个容器,是一个连续数组。Channel提供从文件、网络读取数据的Channel,但是应用程序与 file/socket 之间的读取或写入的数据都必须经由Buffer
在NIO中,读取的数据只能放在Buffer中。同样地,写入数据也是先写入到Buffer中。
在NIO中,Buffer是一个顶层父类,它是一个抽象类,常用的Buffer的子类有:
- ByteBuffer
- IntBuffer
- CharBuffer
- LongBuffer
- DoubleBuffer
- FloatBuffer
- ShortBuffer
对于文件读写,上面几种Buffer都可能会用到
对于网络读写来说,用的最多的是ByteBuffer
Selector:
Selector相当于一个观察者,作用就是用来轮询每个注册的Channel,一旦发现Channel有注册的事件发生,便获取事件然后进行处理。一个选择器可以绑定多个通道.
通道向选择器注册时,需要指定感兴趣的事件,选择器支持以下事件:
-
- SelectionKey.OP_CONNECT
- SelectionKey.OP_ACCEPT
- SelectionKey.OP_READ
- SelectionKey.OP_WRITE
如果你对不止一种事件感兴趣,那么可以用“位或”操作符将常量连接起来,如下:
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
通道向选择器注册时,会返回一个 SelectionKey对象,具有如下属性
-
- interest集合
- ready集合
- Channel
- Selector
- 附加的对象(可选)
没有评论