Tomcat NIO
来源:互联网 发布:淘宝现在还能赚钱吗 编辑:程序博客网 时间:2024/06/05 03:59
Tomcat从6.0版本开始支持NIO, 主体部分代码集中在NioEndpoint这个类当中,Tomcat NIO采用经典的反应堆模式设计,其中核心的组件包括:
1) NioEndpoint (组装器)
2) Acceptor (独立子线程)
3) Poller (独立子线程)(反应堆模式中的multiplexer的wrapper)
4) Worker (独立子线程)(反应堆模式中的dispatcher)
5) SocketProcessor (独立子线程)
6) Handler(反应堆模式中的handler)
Acceptor与Poller中间的数据缓冲是Poller中的events(类型为ConcurrentLinkedQueue<Runnable>);Poller与Worker中间没有数据缓冲,Poller直接将Channel交给Worker,而Worker也是直接将Channel交给SocketProcessor,最终Processor将通道交给Handler完成处理。(reactor模型)
这些组件交互的时序图如下:
Tomcat Nio核心流程由四大部分组成:
1) 组件初始化。
核心组件的组装都是在NioEndpoint中完成。
public class NioEndpoint { // 代表一个服务端TCP服务的管道 protected ServerSocketChannel serverSock = null; // 用于监控通道的多路复用器 protected Poller poller = null; // 默认只配置一个Acceptor protected int acceptorThreadCount = 1; // 初始化并打开端口资源,准备接收请求 public void init() { serverSock = ServerSocketChannel.open();serverSock.socket().bind(addr,backlog); ... } public void start() { init();...// 初始化Acceptor线程 for (int i = 0; i < acceptorThreadCount; i++) { Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i); acceptorThread.setPriority(threadPriority); acceptorThread.setDaemon(daemon); acceptorThread.start(); } // 初始化Poller线程 poller = new Poller(); Thread pollerThread = new Thread(poller, getName() + "-ClientPoller"); pollerThread.setPriority(threadPriority); pollerThread.setDaemon(true); pollerThread.start(); }}
2) 请求接收与注册。
该部分功能主要完成了新请求的接收,以及将请求添加进多路复用器进行进一步事件的监控。完成这部分功能的组件有Acceptor,Poller与NioEndpoint。Acceptor负责循环接收请求,Poller充当了多路复用器的角色,其中核心组件是Selector,而NioEndpoint完成了Acceptor与Poller组件的组装,协调与资源供给。
/** * Acceptor为后台线程,用于接收进入的TCP/IP连接,并将连接的适配器加入待处理队列中等待Poller处理 */ protected class Acceptor implements Runnable { public void run() { // 重复运行直到收到后台发来的关闭请求 while (running) { // 如果Acceptor处于暂停状态,则循环睡眠,直到恢复正常运行状态 while (paused) { try { Thread.sleep(1000); } catch (InterruptedException e) { // Ignore } } try { // 尝试获取新连接请求,此操作肯能会造成线程阻塞,直到有新请求到达 SocketChannel socket = serverSock.accept(); if ( running && (!paused) && socket != null ) { // 具体连接处理逻辑 if (!setSocketOptions(socket)) { try { socket.socket().close(); socket.close(); } catch (IOException ix) { } } } }catch ( IOException x ) { } catch (OutOfMemoryError oom) { releaseCaches(); } catch (Throwable t) { } }//while }//run }核心的函数为setSocketOptions(socket),其中不光完成了socket参数的设置,更重要的工作是将socket包装成为NioChannel装饰模型,并注册给多路复用器Poller以等待事件发生。
/** * 请求注册 */ protected boolean setSocketOptions(SocketChannel socket) { try { // 设置socket为非阻塞模式 socket.configureBlocking(false); Socket sock = socket.socket(); // 设置socket的发送/接收缓存大小, 是否长连接等属性 socketProperties.setProperties(sock); // 从NioChannel缓冲池中获取NioChannel, NioChannel是SocketChannel的一个wrapper, // 目的是对上层屏蔽SSL socket channel和non SSL socket channel的差别 NioChannel channel = nioChannels.poll(); // 无法获取则新建一个NioChannel if ( channel == null ) { // normal tcp setup NioBufferHandler bufhandler = new NioBufferHandler(socketProperties.getAppReadBufSize(), socketProperties.getAppWriteBufSize(), socketProperties.getDirectBuffer()); channel = new NioChannel(socket, bufhandler); // 设置从缓冲中获取的NioChannel } else { channel.setIOChannel(socket); if ( channel instanceof SecureNioChannel ) { SSLEngine engine = createSSLEngine(); ((SecureNioChannel)channel).reset(engine); } else { channel.reset(); } } // 将新接收到的socketChannel注册到selector中 getPoller0().register(channel); } catch (Throwable t) { return false; } return true; }前面的大段代码是设置socket参数以及将socket包装成为NioChannel,核心调用发生在getPoller0().register中,Acceptor通过NioEndpoint获得Poller以后,将包装好的NioEndpiont加入到Poller中的等待队列中,等待Poller处理注册事件
3) 事件注册,分拣与派发。
这是整个NIO事件框架的核心,有Poller完成。Poller中包含一个等待队列,其他组件可以通过向等待队列中添加需要注册读写事件的通道,Poller会循环处理队列中的事件; 并在处理完事件后调用多路复用器的查询功能,查看发生感兴趣事件的通道,并进行事件派发给合适的处理器SocketProcessor。
// 后台线程,循环处理待处理事件队列中的事件 public class Poller implements Runnable { // 多路复用器 protected Selector selector; // 待处理事件队列 protected ConcurrentLinkedQueue<Runnable> events = new ConcurrentLinkedQueue<Runnable>(); // 唤醒多路复用器的条件阈值 protected AtomicLong wakeupCounter = new AtomicLong(0l); // 停止Poller线程前所需要打开的插销 protected CountDownLatch stopLatch = new CountDownLatch(1); // 将新来的socket请求包装成统一的事件类型PollerEvent后,加入待处理事件队列中public void register(final NioChannel socket) { socket.setPoller(this); KeyAttachment key = keyCache.poll(); final KeyAttachment ka = key!=null?key:new KeyAttachment(); ka.reset(this,socket,getSocketProperties().getSoTimeout()); // 从事件池中取出一个空闲的事件对象,并使用新连接初始化事件对象 PollerEvent r = eventCache.poll(); ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into. if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER); else r.reset(socket,ka,OP_REGISTER); // 加入事件队列 addEvent(r); } // 外部组件可以通过该接添加待处理事件public void addEvent(Runnable event) { events.offer(event); // 如果当前事件队列中没有事件,则唤醒处于阻塞状态的selector if ( wakeupCounter.incrementAndGet() == 0 ) selector.wakeup(); } // Poller是实现了Runnable接口的后台线程,实现了run方法public void run() { // 无线循环处理事件,直到接收到关闭命令 while (running) { try { // 如果Endpoint处于暂停状态,则将线程暂时进入睡眠状态 while (paused && (!close) ) { try { Thread.sleep(500); } catch (InterruptedException e) { // Ignore } } boolean hasEvents = false; hasEvents = (hasEvents | events()); // Time to terminate? if (close) { timeout(0, false); stopLatch.countDown(); return; } int keyCount = 0; try { if ( !close ) { if (wakeupCounter.get()>0) { //if we are here, means we have other stuff to do //do a non blocking select// 以非阻塞方式查看多路通道是否有事件发生 keyCount = selector.selectNow(); }else { wakeupCounter.set( -1);// 查看多路通道是否有事件发生,超过指定时间则立即返回 keyCount = selector.select(selectorTimeout); } wakeupCounter.set(0); } if (close) { timeout(0, false); stopLatch.countDown(); selector.close(); return; } } catch ( NullPointerException x ) { continue; } catch ( CancelledKeyException x ) { continue; } catch (Throwable x) { continue; } // 此时如果没有感兴趣的通道事件发生,则再次执行待处理队列中的事件, // 如果有事件待处理,后面就暂时不对通道执行超时检测 if ( keyCount == 0 ) hasEvents = (hasEvents | events()); Iterator iterator = keyCount > 0 ? selector.selectedKeys().iterator() : null; // 循环处理有事件发生的channel while (iterator != null && iterator.hasNext()) { SelectionKey sk = (SelectionKey) iterator.next(); KeyAttachment attachment = (KeyAttachment)sk.attachment();// 更新通道最近一次发生事件的时间,防止因超时没有事件发生而被剔除出selector attachment.access(); iterator.remove();// 具体处理通道的逻辑 processKey(sk, attachment); }//while // 多路复用器每执行一遍完整的轮询便查看所有通道是否超时,对超时的通道将会被剔除出多路复用器 timeout(keyCount,hasEvents); } catch (OutOfMemoryError oom) { releaseCaches(); } }//while synchronized (this) { this.notifyAll(); } stopLatch.countDown(); } // 处理事件队列中的所有待处理事件public boolean events() { boolean result = false; Runnable r = null; // 等待队列中是否有尚未处理的事件 result = (events.size() > 0); // 将等待队列中的事件一一处理 while ( (r = (Runnable)events.poll()) != null ) { try { // 运行事件处理逻辑,此处将事件设计成为Runnable的意义是将具体的事件处理逻辑和事件框架分开 r.run(); // 事件处理完成后,将事件对象归还对象池 if ( r instanceof PollerEvent ) { ((PollerEvent)r).reset(); eventCache.offer((PollerEvent)r); } } catch ( Throwable x ) { log.error("",x); } } return result; } // 处理selector检测到的通道事件protected boolean processKey(SelectionKey sk, KeyAttachment attachment) { boolean result = true; try { if ( close ) { cancelledKey(sk, SocketStatus.STOP, false); } else if ( sk.isValid() && attachment != null ) { // 确保通道不会因超时而被剔除 attachment.access(); sk.attach(attachment); NioChannel channel = attachment.getChannel(); // 处理通道发生的读写事件 if (sk.isReadable() || sk.isWritable() ) { if ( isWorkerAvailable() ) { // 在通道上注销对已经发生事件的关注 unreg(sk, attachment,sk.readyOps()); // 具体通道处理逻辑 boolean close = (!processSocket(channel)); if (close) { cancelledKey(sk,SocketStatus.DISCONNECT,false); } } else { result = false; } } } else { // 解除无效通道 cancelledKey(sk, SocketStatus.ERROR,false); } } catch ( CancelledKeyException ckx ) { cancelledKey(sk, SocketStatus.ERROR,false); } catch (Throwable t) { log.error("",t); } return result; } }
// 待处理事件队列中事件模型 public class PollerEvent implements Runnable { protected NioChannel socket; protected int interestOps; protected KeyAttachment key; public PollerEvent(NioChannel ch, KeyAttachment k, int intOps) { reset(ch, k, intOps); } public void reset(NioChannel ch, KeyAttachment k, int intOps) { socket = ch; interestOps = intOps; key = k; } public void reset() { reset(null, null, 0); } public void run() { // 该socket第一次注册到多路复用器selector中,完成对该socket的读事件注册 if ( interestOps == OP_REGISTER ) { try { socket.getIOChannel().register(socket.getPoller().getSelector(), SelectionKey.OP_READ, key); } catch (Exception x) { log.error("", x); } // socket之前已经注册到了selector中,更新对该socket所感兴趣的事件 } else { final SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector()); try { boolean cancel = false; if (key != null) { final KeyAttachment att = (KeyAttachment) key.attachment(); if ( att!=null ) { // 刷新事件的最后访问时间,防止事件超时 att.access(); int ops = key.interestOps() | interestOps; att.interestOps(ops); key.interestOps(ops); } else { cancel = true; } } else { cancel = true; } // socket异常,从多路复用器中剔除 if ( cancel ) getPoller0().cancelledKey(key,SocketStatus.ERROR,false); }catch (CancelledKeyException ckx) { try { getPoller0().cancelledKey(key,SocketStatus.DISCONNECT,true); }catch (Exception ignore) {} } }//end if }//run }
具体的事件派发是通过Poller.run方法中调用的函数processSocket完成。
public class NioEndpoint { protected boolean processSocket(NioChannel socket, SocketStatus status, boolean dispatch) {try { // 将有事件发生的通道交给worker线程处理 getWorkerThread().assign(socket, status);} catch (Throwable t) { log.error(sm.getString("endpoint.process.fail"), t); return false;}return true; }}processSocket实现在NioEndpoint中,Poller通过NioEndpoint的协调,将发生事件的通道交给工作线程Worker来进一步处理。整个事件框架的工作就到此结束,下面就是事件的处理。
4) 事件处理。
事件处理的核心组件有Worker,SocketProcessor和Handler。Poller将发生事件的通道交给Worker(注意这里是推的模式,而在Nginx和Jetty中采用的是拉的模式),而Worker由交给SocketProcessor处理,SocketProcessor寻找合适的Handler处理器做最终处理。
// 工作线程,负责将通道交给合适的处理器处理 protected class Worker implements Runnable { protected Thread thread = null; protected boolean available = false; protected Object socket = null; protected SocketStatus status = null; /** * 推模型接口 */ protected synchronized void assign(Object socket) { // Wait for the Processor to get the previous Socket while (available) { try { wait(); } catch (InterruptedException e) { } } // Store the newly available Socket and notify our thread this.socket = socket; status = null; available = true; notifyAll(); } /** * 等待Poller推送通道过来 */ protected synchronized Object await() { // Wait for the Connector to provide a new Socket while (!available) { try { wait(); } catch (InterruptedException e) { } } // Notify the Connector that we have received this Socket Object socket = this.socket; available = false; notifyAll(); return (socket); } public void run() { // 循环处理请求,直到容器收到关闭信号 while (running) { NioChannel socket = null; SelectionKey key = null; try { // 等待分拣器将发生事件的管道分配给工作线程处理 Object channel = await(); if (channel == null) continue; // 将通道交给处理器处理 socket = (NioChannel)channel; SocketProcessor sc = processorCache.poll(); if ( sc == null ) sc = new SocketProcessor(socket,status); else sc.reset(socket,status); sc.run(); }catch(CancelledKeyException cx) { if (socket!=null && key!=null) socket.getPoller().cancelledKey(key,null,false); } catch (OutOfMemoryError oom) { releaseCaches(); } finally { recycleWorkerThread(this); } } } /** * 启动工作线程 */ public void start() { thread = new Thread(this); thread.setName(getName() + "-" + (++curThreads)); thread.setDaemon(true); thread.setPriority(getThreadPriority()); thread.start(); } }
protected class SocketProcessor implements Runnable { protected NioChannel socket = null; protected SocketStatus status = null; public SocketProcessor(NioChannel socket, SocketStatus status) { reset(socket,status); } public void reset(NioChannel socket, SocketStatus status) { this.socket = socket; this.status = status; } public void run() { NioEndpoint.this.activeSocketProcessors.addAndGet(1); SelectionKey key = null; try { key = socket.getIOChannel().keyFor(socket.getPoller().getSelector()); if (key!=null) handshake = socket.handshake(key.isReadable(), key.isWritable()); // 交给Handler处理请求 boolean closed = (status==null)?(handler.process(socket)==Handler.SocketState.CLOSED) : (handler.event(socket,status)==Handler.SocketState.CLOSED); if (closed) { // Close socket and pool try { KeyAttachment ka = null; if (key!=null) { ka = (KeyAttachment) key.attachment(); if (ka!=null) ka.setComet(false); socket.getPoller().cancelledKey(key, SocketStatus.ERROR, false); } if (socket!=null) nioChannels.offer(socket); socket = null; if ( ka!=null ) keyCache.offer(ka); ka = null; }catch ( Exception x ) { log.error("",x); } } }catch ( Throwable t ) { log.error("",t); socket.getPoller().cancelledKey(key,SocketStatus.ERROR,false); } finally { socket = null; status = null; //return to cache processorCache.offer(this); NioEndpoint.this.activeSocketProcessors.addAndGet(-1); } } }
- Tomcat NIO
- tomcat nio
- Tomcat NIO
- tomcat nio
- Tomcat nio
- Apache Tomcat NIO设置
- 浅析tomcat nio 配置
- 浅析Tomcat NIO 配置
- tomcat nio配置
- 浅析tomcat nio 配置
- 浅析Tomcat NIO 配置
- Apache Tomcat NIO设置
- 浅析Tomcat NIO 配置
- tomcat设置nio模式
- tomcat NIO工作流程
- 浅析tomcat nio 配置
- tomcat bio nio apr
- tomcat 开启nio
- web性能监控与分析
- 安装配置Apache Tomcat V6.0
- osgintersection例子
- windows tomcat配置大全[详细]
- Tomcat配置Context
- Tomcat NIO
- hdu 4411 Arrest
- Eclipse开发调试ARM裸机程序(二)LED_从ARM到GNU
- 最近学struts2和hibernate 在tomcat 运行有错误。。百思不得求解。。。求高手指点:
- Delphi Dll 创建和使用(2)
- Android---把内容发送给另外一个应用程序
- NYOJ 104 矩阵最大和 - 简单动态规划
- 一个简单的AXIS远程调用Web Service示例
- HDU 1561 The more, The Better - 依赖背包+树形dp基础