NIO框架的目的在于屏蔽了底层的NIO操作,使得程序员只需要关注本身的业务逻辑即可,这样可以大大的提高开发效率,所有框架推出的目的不外乎都是这个目的。下面我们来看下如何去做一个NIO框架,一个典型的网络通信过程如下:
这里我们主要看下服务端的流程该如何处理:
1,线程模型
传统的方式为针对每一个新来的连接建立一个线程去处理,这样的方式太低效了,这里我们仿照netty的方式,采用reactor线程模型:
a,用一个NIO acceptor线程专门用于监听服务端,接收客户端的 TCP 连接请求,然后将连接分配给工作线程,由工作线程来监听具体的读写事件,这就相当于1个boss和N个worker撑起了整个公司(NIO框架)一样。
b,网络I/O操作有多个worker线程负责,由worker线程负责消息的读取,编码,解码发送等操作
c,一个worker可以处理N个连接通道,一个连接只对应一个worker
2,事件处理
整个流程,我们都建立在基于事件通知的机制上,为此我们需要一个监听者Listener,当我们感兴趣的事件到来时,进行相应的事件处理:
public interface Listener {//处理各种事件
public void channelActive(ConnectionContext ctx);
public void channelRead(ConnectionContext ctx,Object msg) ;
public void channelWrite(ConnectionContext ctx,Object msg) ;
public void channelClose(ConnectionContext ctx);
}
以及一个EventFire,用于触发各种事件的处理:
public interface EventFire {
public void fireChannelActive(ConnectionContext ctx);
public void fireChannelRead(ConnectionContext ctx, Object msg);
public void fireChannelWrite(ConnectionContext ctx, Object msg);
public void fireChannelClose(ConnectionContext ctx);
}
由于我们的事件处理器只处理感兴趣的事件,而上面是接口的定义,我们采用一个抽象类EventAdapter用作中间的默认实现:
public abstract class EventAdapter implements Listener,EventFire {
public EventAdapter next;
。。。。。。
}
为什么它要实现EventFire接口呢?因为当前一个处理结束后,需要通知到后一个处理,所以这里的Eventadapter实际上既是一个事件处理器,又是一个事件通知器,它主要起到适配作用。EventAdapter是一个链表,因为在上图中的处理流程中,再进行实际业务逻辑处理时可能需要一大堆的前置操作,所以这里我们采用了类似pipeline的模式,我们定义了一个EventManager来管理这些Adapter:
public class EventManager implements EventFire {
private EventAdapter head;
private EventAdapter tail;
@Override
public void fireChannelActive(ConnectionContext ctx) {
head.channelActive(ctx);
}
@Override
public void fireChannelRead(ConnectionContext ctx, Object msg) {
head.channelRead(ctx,msg);
}
@Override
public void fireChannelWrite(ConnectionContext ctx, Object msg) {
head.channelWrite(ctx,msg);
}
@Override
public void fireChannelClose(ConnectionContext ctx) {
head.channelClose(ctx);
}
public synchronized void addLast(EventAdapter adapter){
if(head == null){
head = adapter;
tail = adapter;
}else{
tail.next = adapter;
tail = adapter;
}
}
}
它主要负责维护EventAdapter,以及触发通知事件,整个事件的处理机制如下:
这里我们的EventManager(pipeline)是共用的,就是所以新来的连接都是共用一个EventManager,如果有需求可以,可根据实际情况进行deepcopy,为每一个连接建立一个对应的EventManager。
3,主要的实现类
NioServer,服务段实现类,就是我们的boss,他服务监听请求,然后通知worker干活:
public class NioServer extends Thread {
private int port = 8888;//default 8888
private int worker_size = 16;//default 16
private AtomicBoolean stopped = new AtomicBoolean(false);
private Selector selector;
private ServerSocketChannel serverSocketChannel;
private Worker[] workers;
private int worker_index = 0;
private EventManager listenerManager;
}
有了这个类,程序员只需要在EventManager中添加自己的处理handler即可,不用再关注连接的具体处理。
NioClient:NIO客户端,程序也只需关注添加自己的业务逻辑处理handler即可:
public class NioClient {
private SocketChannel socketChannel;
private Selector selector;
private Worker worker;
private EventManager manager;
}
ConnectionContext:连接上下文:
public class ConnectionContext {
private Worker worker;//工作线程
private SocketChannel socketChannel;//通道
private EventManager eventManager;
}
Worker:实际干活者负责注册server传过来的socket连接,监听读事件,处理写操作等:
public class Worker extends Thread {
private AtomicBoolean started = new AtomicBoolean(false);
private Queue<Runnable> taskQueue;//任务队列
private Selector selector;//选择器
private ByteBuffer byteBuffer = ByteBuffer.allocate(2048);
public void run() {
while(true){
try{
if(!started.get()){
break;
}
runTasks();
selector.select(10);
processSelectedKeys();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
worker维护了一个任务队列,然后在一个while循环中执行任务,并处理select结果。
以上是大致的介绍,更详细的代码已上传github:https://github.com/reverence/myNIO