事件驱动模型的角度来看 JAVA NIO

作者 chauncy 日期 2016-12-09
事件驱动模型的角度来看 JAVA NIO

事件驱动模型的角度来看 JAVA NIO

事件驱动模型的角度来看 java nio,先作知识的简单铺垫,
1,阻塞非阻塞
阻塞式I/O模型:
(1)等待数据准备好;
(2)从内核向进程复制数据。
2,非阻塞式I/O: 当所请求的I/O操作非得把本进程投入睡眠才能完成时,不要把进程投入睡眠,而是返回一个错误。进而不断的通过轮询方式来获取正确的结果
3,I/O多路复用:虽然I/O多路复用的函数也是阻塞的,但是其与以上两种还是有不同的,I/O多路复用是阻塞在select,epoll这样的系统调用之上,而没有阻塞在真正的I/O系统调用。

阻塞,非阻塞:进程/线程要访问的数据是否就绪,进程/线程是否需要等待;
同步,异步:访问数据的方式,同步需要主动读写数据,在读写数据的过程中还是会阻塞;异步只需要I/O操作完成的通知,并不主动读写数据,由操作系统内核完成数据的读写。

这里只是简单的概述而已

4.nio 主要模块介绍

selector 主要负责监听io事件,channel这里可以简单理解对于数据的操作,我觉得把它理解为socket 吧好理解一点,比如发送数据和读取数据,buffer 则是将数据进行读入和读取,对于buffer的理解可以参照java io 读取的文件的buffer
5,java nio 开发流程架构

6,代码的实现:
(1)Reactor 构造函数

  public Reactor(int port) throws IOException {
  selector = Selector.open();
  serverSocketChannel = ServerSocketChannel.open();
  serverSocketChannel.bind(new InetSocketAddress(port));
  serverSocketChannel.configureBlocking(false);
  SelectionKey selectionKey =  serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
  selectionKey.attach(new Acceptor());

}

Reactor是一种事件驱动模型;使用Selector 有如下方法
a. public static Selector open();
该方法获取一种io 多路复用的机制,比如linux 2.6+平台java提供的是epoll 机制,mac os x 提供的是kqueue机制,这里就不对io 复用探讨
b. 获取server channel 对象进行绑定监听端口
c. 设置服务方式为非阻塞方式
d. 将channel 注册到selector 上,让selector 去管理我们关心的io
e. SelectionKey 理解为selector 和 channel 之间的抽象

(2)实现Runable接口的run 方法

public void run() {
      try {
          while (!Thread.interrupted()) {
              selector.select();
              Set selected = selector.selectedKeys();
              Iterator it = selected.iterator();
              //循环的遍历,可以操作的事件
              while (it.hasNext())
                  //将读取到的事件进行分发
                  dispatch((SelectionKey)(it.next()));
              selected.clear();
          }
      } catch (IOException e){

      }
  }

Selector是java nio 中的多路复用器,配合SelectionKey使用,SelectionKey代表着一个Channel和Selector的关系的抽象,Channel向Selector注册的时候产生,由Selector维护。Selector维护着三个SelectionKey的集合:

private Set publicKeys; 当有SelectionKey关联的Channel有Channel向Selector注册的IO事件就绪的时候并且有select操作,对应的SelectionKey会被放到publicSelectedKeys中。因为这个集合中的SelectionKey可以通过直接调用Set的remove将SelectionKey移除。
private Set publicSelectedKeys 当有SelectionKey关联的Channel有Channel向Selector注册的IO事件就绪的时候并且有select操作,对应的SelectionKey会被放到publicSelectedKeys中。因为这个集合中的SelectionKey可以通过直接调用Set的remove将SelectionKey移除。
private final Set cancelledKeys 当有已经向Selector注册的Channel发生degistered的时候,SelectionKey将被放到这个集合,并且在下一次select的时候被从所有的集合中移出。

(3)Acceptor 类:

class Acceptor implements Runnable {

    public void run() {
        try {
            SocketChannel c = serverSocketChannel.accept();
            if (c != null)
                new Thread(new Handler(c,selector)).start();
        }
        catch(IOException ex) {
        }
    }
}

这里就是获取到相应的channel 对象,然后交给处理线程 handler去处理

(4)dispatch 方法:

private void dispatch(SelectionKey k) {
     Runnable r = (Runnable)(k.attachment());
     if (r != null)
         r.run();
 }

这里 SelectionKey .attachment 使用的是AtomicReferenceFieldUpdater 这个类的方法 原子更新
(5)Handler 类

public Handler(SocketChannel socketChannel, Selector selector) throws IOException {
    this.socketChannel = socketChannel;
    socketChannel.configureBlocking(false);
    selectionKey = socketChannel.register(selector, 0);
    selectionKey.attach(this);
    selectionKey.interestOps(SelectionKey.OP_READ);
    selector.wakeup();

}

public void run() {
        if (state == READING) read();
        else if (state == SENDING) send();
}

逻辑,就是让selector 继续侦听我们注册的事件,然后对于数据进行read write 操作而已

代码地址

至此整个reactor 的基本架构已经结束,此次纪录为后面的netty 做准备