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);                }        }    }

原创粉丝点击