网络IO的分类和思考

先看IO分类

同步阻塞IO: 

在此种方式下,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行。JAVA传统的IO模型属于此种方式!很多开发人员以为只要新启动一个线程并在此线程中使用同步堵塞IO就是异步了,实际上这不是异步,线程上还是标准的同步!

同步非阻塞IO: 

在此种方式下,用户进程发起一个IO操作以后边可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费。其中目前JAVA的NIO就属于同步非阻塞IO,所以不要认为Java NIO是真正的异步IO,只是“半”异步IO,或者叫做伪异步IO,所以基于NIO的通信框架,被很多人看成是高性能的通信框架,其实还未有发挥到单机性能的极致,因为它们的基础NIO本身就不是真正的异步IO。但是1.7+引入了AIO的支持,称之为NIO2.0, 或许让Java往真异步IO迈进了一步!

异步阻塞IO:

此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成,等内核完成IO操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问IO是否完成,那么为什么说是阻塞的呢?因为此时是通过select系统调用来完成的,而select函数本身的实现方式是阻塞的,而采用select函数有个好处就是它可以同时监听多个文件句柄(select属于同步操作。因为select之后,进程还需要读写数据),从而提高系统的并发性!但是select的问题大家也知道,就是受句柄数的限制!

异步非阻塞IO:

在此种模式下,IO性能表现是最好的!用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,读写IO完全是由操作系统完成的,事后用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。目前Java中还没有支持此种IO模型,Linux也支持的不好(poll,epoll都是类异步堵塞IO),windows倒是支持的不错,所以在设计一个异步IO框架时需要在底层考虑OS的差异,并在上层屏蔽这样的差异,保持API的一致性!

 

结论与思考:

只有异步非堵塞IO才是真正的异步IO,如果再加上Reactor或者Proactor这样的高性能并发模式,并加上较好的抽象设计,能够较好的封装出来一个高性能框架,但是问题是它不是所有OS都可以支持,所以我的想法,

第一种方案:

提供两种版本的框架C++和Java的,并且都分层三层,第一层是应用层,提供高层抽象的API,屏蔽一切和通信概念或者细节相关的知识,只提供应用层面的framework, 分成C++和Java版本的, 这两个版本都共用第二层和第三层的实现,第2和第3都完全使用C++来实现,第2层提供一种IO多路复用分离器(Proactor),第三层实现通信机制(不同OS可能使用不同的异步IO机制),它能够根据OS提供优化的通信机制,并且为第二层屏蔽通信细节。

 

第二种方案:

不过需要对NIO2.0进行一些验证,是否是真正的异步IO,如是,那么第二种方案就简单了,全部是Java开发,重点在框架设计上-能不能发挥最大的性能优势,并且在各种场合下适用。

 

另外即使是完全的异步IO,它也避免不了内存复制的问题,就是从OS内核缓冲区复制数据到用户区的问题,它的性能取决于很多硬件因素和软件因素,所以我也希望避免这种内存复制,做到零 copy的水平。

对于内存零Copy,方案是:

所有现代操作系统都使用虚拟内存。虚拟内存意为使用虚假(或虚拟)地址取代物理(硬件

RAM)内存地址。这样做好处颇多,总结起来可分为两大类:

1. 一个以上的虚拟地址可指向同一个物理内存地址。

2. 虚拟内存空间可大于实际可用的硬件内存。

设备控制器不能通过 DMA 直接存储到用户空间,但通过利用上面提到的第一

项,则可以达到相同效果。把内核空间地址与用户空间的虚拟地址映射到同一个物理地址,这样,

DMA 硬件(只能访问物理内存地址)就可以填充对内核与用户空间进程同时可见的缓冲区。

这样就省去了内核与用户空间的往来拷贝,但前提条件是,内核与用户缓冲区必须

使用相同的页对齐,缓冲区的大小还必须是磁盘控制器块大小(通常为 512 字节磁盘扇区)的倍

数。

日后,逐渐滴在博客中将此框架的设计和实现发表出来,并且开源,作为个人项目出现,并和朋友们一道丰富和优化它。

 

 

 

 

 

 

 

猜你喜欢

转载自leogao-java.iteye.com/blog/2161186