学习Java nio有段时间了,但是其中有些概念一起没有搞太明白,如channel注册后,selector如何处理的等。 无奈搜索出来的中文资料对这些都没有谈及过。 最终在看过JavaWorld的这篇文章后重看SUN官方文档后,才算把这个机制弄清楚了。下面的内容多来自以上面提及的两篇文章。
关于Channel
1.ServerSocketChannel 创建服务器端Socket,监听某特定端口,接收客户端请求。
2.SocketChannel 用于描述在一个客户端连接,既可以是发起请求的客户端本身的连接,也可以在表示该请求在服务器端与之对应的SocketChannel连接对象。几乎所有的数据读写操作都在这个类中完成。可以有block和nonblock两种方式。
3.Selector 用于管理Channel,通过select轮循注册在该Selector上的所有Channel,并负责dispatch事件。
关于ByteBuffer
ByteBuffer分为两种,一种为direct,一种为Nondirect. 使用direct buffer时JVM会分配一个连续的内存块,直接通过native access mothed进行读写操作。而nondirect buffer时,JVM通过数组访问的方式来访问其中数据。在Channel对Buffer进行操作时,最终只操作direct buffer。如果Channel接收到的是一个nondirect buffer,它会创建一个direct buffer,然后将nondirect buffer中的数据拷贝到这个direct buffer中,然后调用系统方法进行读写。
关于Selector与Reactor模式
Reactor的定义:Reactor allows applications to decouple event arrival from event handling. Events arrive at arbitrary times but are not immediately dispatched. Instead, a Reactor keeps track of the events until the handlers ask for them.
在java nio中,Selector就是扮演这个Reactor角色。每个SelectableChannel向Selector注册感兴趣的事件,当事件发生时,Selector记录并跟踪(track)这些事件。当应用程序调用Selector的select方法时,Selector轮循(poll)所有注册过的SelectableChannels,查看是否有它们感兴趣的事件发生。 一个SelectableChannel可以向不同的Selector中注册不同的事件。每种类型的SelectableChannel所能注册的事件也不一样。各Channel能注册的事件如下图示:
ServerSocketChannel |
OP_ACCEPT |
SocketChannel |
OP_CONNECT, OP_READ, OP_WRITE |
DatagramChannel |
OP_READ, OP_WRITE |
Pipe.SourceChannel |
OP_READ |
Pipe.SinkChannel |
OP_WRITE |
每一个SelectableChannel在Selector中于SelectionKey对象体现. Selector在中包括三种类型的key:key set 表示所有当前注册了的key,selected-key set 表示前一次prior selection 操作得到的key,即包含有事件的key,cancelled-key set 已经cancel了,但是channel还未撤消的key. 这三者之间存在有一个状态机,如图示(Dia画的图):
Section operation具体做的操作如下:
1、清空所有cancelled-key set中的key,并将其对应的channel撤消掉;
2、查看每个channel所注册的事件是否发生,如果发生做如下处理:
a.该channel已经存在于selected-key set中,则在其上加上新到的事件
b.在selected-key set中不存在,则加入
3、如果在处理第2步过程中有keys被加入cancelled-key set,则执行1步骤
Selector 与 ServerSocketChannel | SocketChannel交互的过程图,下面这个图展示了Selector做的过程。从图中可以看到接收请求的仍然是Channel,只不过接收到请求后在Channel中并不处理。在Selector的select()等方法被调用后,Selector去轮循每一个Channel,进行相应事件的统一调度。 这样就将接收请求和处理请求完全解耦了。业务逻辑的处理不需要了解如何获取请求,因此可以采用事件驱动(Event-Driver)方式来进行后面的处理,如MINA框架中所做的那样。

