Tomcat NIO

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

    }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值