Freewind @ Thoughtworks scala java javascript dart 工具 编程实践 月结 math python english [comments admin] [feed]

(2011-09-13) Java nio 初体验

广告: 云梯:翻墙vpn (省10元) 土行孙:科研用户翻墙http proxy (有优惠)

Java的nio是jdk5推出的东西,距现在已经五六年了。我虽然曾经使用过Mina(一个使用事件驱动的nio框架)做过一些项目,但实际上对nio的了解还是很模糊,只是知道它的特点是“非阻塞”。

近期打算研究一下Netty(它跟Mina很像,是同一个作者的作品),所以先研究下Nio。网上的中文资料相当少,大多讲得不清不楚,给的示例代码更是看得人头晕。好在群友“羊八井”推荐了我一本好书:《Java network programming 3rn edition》,它的第12章就是专讲nio的。这本书写得不错,详细而清晰,把重点都讲到了。我花了三四个小时,把这一章读完,基本上对nio有了一个比较感性上的认识。

nio是new IO,它的特点是“非阻塞”。相对于之前的阻塞IO,最大的好处是提高了性能。只要抓住两点:“非阻塞”与“性能”,nio就好理解了。

以前使用Java进行socket编程时,等待客户端连接的accept()和读取数据的read()方法,都是“阻塞”的。“阻塞”的意思就是说,这两个函数都是非常固执的,一旦调用,所在线程就会卡在那里,直到取到了数据,或者超时,或者出错。意味着一个线程一次只能做一件事情。如果客户端连接只有一个,这种方式倒十分简便,但一旦有多个连接,服务器就必须用多个线程分别处理。当连接比较多(比如上千时),就会有大量的时间浪费在线程间的切换上,性能比较差。对于像web server这样的程序来说,是难以接受的。

而nio是非阻塞的,它的accept()和read()方法,调用后马上返回,线程不会卡住。这样我们就可以在一个线程中,处理多个连接(当然也可以使用更多的线程,更充分地利用cpu)。那么,我们如何知道有连接来了,或者拿到数据了呢?答案是轮询,判断返回值,办法虽笨,但是有用。

nio还是“事件驱动”的,这与“非阻塞”正好搭配。我们可以给每个连接注册想要监听的事件,如accept, connect, read, write,然后就去做别的事。如果没事可做,就轮询,调用selectKeys()方法检查当前发生的事件。当我们监听的事件发生了(比如监听了read事件,并且有数据可读时),即可得到对应的SelectKey对象。我们可以从该对象身上拿到对应的连接,做想做的事情,比如读数据写数据,注册新的监听事件等。

该“性能”了。nio为了提高数据交换的性能,提供了一些buffer类,用于缓存读取的数据及输出的数据。这些buffer长度固定,并且用一些属性如position, limit, mark等标示当前可用的数据,以及一些方法clear(), flip(), rewind(), mark(), reset(), compact()等,改变这些属性。这里有些复杂,一定要理解清楚,才不会用错。但理解起来也不难,因为它搞这么复杂就是为了复用数据,为了复用肯定要有一些方法来记录哪些数据被使用了,哪些未被使用。这样在read和write时,才能正确拿到可用的数据。nio中,所有需要交换数据的地方,都在用buffer,所以相关操作一定要弄明白,不然寸步难行。

最后就是nio中几个重要的概念:Channel, Selector, SelectionKey, Buffer。Channel就像是门,Selector是看门的,SelectionKey是事件记录,而Buffer是送货的推车。

整个流程是这样的:首先我们在墙上装几个门(Channel),然后请一个看门人(Selector)过来,告诉他要看哪几个门(注册),还要记录下哪些情况。然后我就去干自己的事了,过一会儿过来看看,对看门人说:把你的记录本给我看看。如果是空的,就还给他,接着干自己的事。如果有记录(SelectionKey),就一一查看:A门有人送货,B门有人提货,C门有客上门。这时可能会用到推车(Buffer)来装货卸货。处理完这一批事件后,我把这些记录撕掉(key.remove()),还给看门人,告诉他还要记录哪些新情况(重新注册)。然后我再去做其它事,过一会儿再回来看看。。。

从这里可以看出,nio还是比较底层的,属于基础设施。因为它虽然可以告诉我们发生了哪些事件,但是不能给每个事件设置回调函数,必须手动轮询。所以就有了如Mina、Netty这样的基于nio的框架。它们最大的作用,就是抽象出多个事件,让我们设置对应的回调函数。之前我还需要过一会儿过来问问看门人,而现在,我只需要在一开始给看门人交待好“发生了什么事情,你就去做什么事”,然后就可以什么也不用管了。

Nio的重点差不多就这样了,剩下的就是要熟悉那些api的使用。原理相比以前的阻塞io要复杂了一点,但是api却复杂不少。

comments powered by Disqus