再调用processSocket处理读事件,之后处理写事件
protected void processKey(SelectionKey sk, NioSocketWrapper attachment) { try { if ( close ) { // 如果Poller关闭则关闭和释放和此连接相关的资源 cancelledKey(sk); } else if ( sk.isValid() && attachment != null ) { if (sk.isReadable() || sk.isWritable() ) { if ( attachment.getSendfileData() != null ) { processSendfile(sk,attachment, false); } else { // 取消注册事件 // sk.interestOps()& (~readyOps) unreg(sk, attachment, sk.readyOps()); boolean closeSocket = false; // Read goes before write 先读后写 if (sk.isReadable()) { // 关键代码,调用processSocket方法处理读事件 if (!processSocket(attachment, SocketEvent.OPEN_READ, true)) { closeSocket = true; } } if (!closeSocket && sk.isWritable()) { if (!processSocket(attachment, SocketEvent.OPEN_WRITE, true)) { closeSocket = true; } } if (closeSocket) { cancelledKey(sk); } } } } else { //invalid key cancelledKey(sk); } } catch ( CancelledKeyException ckx ) { cancelledKey(sk); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); log.error("",t); } } processSocket 真-处理I/O事件processSocket定义在org.apache.tomcat.util.net.AbstractEndPoint中, 也就是意味着无论你采用的是BIO还是NIO或者NIO2最终读写数据都是调用此方法
从代码中可以看出,依然是对象池,依然是再次封装(套娃),并将其提交到线程池中执行,接下来的内容就不再本次讨论范围内呢。
public boolean processSocket(SocketWrapperBase<S> socketWrapper, SocketEvent event, boolean dispatch) { try { if (socketWrapper == null) { return false; } SocketProcessorBase<S> sc = processorCache.pop(); if (sc == null) { sc = createSocketProcessor(socketWrapper, event); } else { sc.reset(socketWrapper, event); } Executor executor = getExecutor(); if (dispatch && executor != null) { executor.execute(sc); } else { sc.run(); } } catch (RejectedExecutionException ree) { getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree); return false; } catch (Throwable t) { ExceptionUtils.handleThrowable(t); // This means we got an OOM or similar creating a thread, or that // the pool and its queue are full getLog().error(sm.getString("endpoint.process.fail"), t); return false; } return true; } 总结 Tomcat的NIO模型手抖了,线不怎么♂
LimitLatch 为所有的Acceptor共用,用来限制当前的最大连接数
Acceptor 以阻塞的形式来接收新连接,并将其封装成PollerEvent对象提交到Poller中
Poller 接收来自Acceptor的PollerEvent并注册读事件,以及轮询和其绑定的客户端Socket有无读事件,如果有则执行进一步操作,将其提交到其他地方执行处理(解析Http协议)
思想迁移学习源码就是为了学习其设计思想. -- 沃兹及.硕德
对象池化 池化对象、池化连接可以大大降低新建对象以及GC所带来的消耗,当需要使用从池中取出来重新设置相关值即可
环形队列 虽然这玩意不新鲜,但配合上原子类,就可以在高并发的情况,高效的获取队列中的下一个元素(环形队列中索引溢出的处理在之前我是没有考虑到的)
阻塞获取链接,非阻塞处理IO事件 与Reactor模型形成强烈的对比,学习NIO的时候思维被限制住了,认为非阻塞的获取连接会获得更高的性能,但现在情况不一定了(还没测试,哪位老哥试了告诉我一下)
关键操作时,对标志位进行检测 如果你要通过一个标志变量来控制你的线程,且线程循环一次需要相对较长的时间(你代码太长,操作太多)那么最好在执行关键操作之前对你的标志变量进行检查,来决定是否要改变线程的行为(康康poller和Acceptor的代码)