Jetty NIO (一)

启动Jetty的命令行为

java -jar start.jar --ini=start.ini

Start.jar会调用org.mortbay.xml.XmlConfiguration的main()方法,如果没有提供启动参数则使用缺省的jetty.xml。代码首先实例化XmlConfiguration对象,然后调用其configure()方法去加载定义在xml文件里的类org.mortbay.jetty.Server,然后实例化Server类。之后所有的一切就从Server类的doStart()方法开始。

public class Server extends HandlerWrapper implements Attributes {

    private ThreadPool _threadPool;
    private Connector[] _connectors;

    public Server(int port)
    {
        setServer(this);
        Connector connector=new SelectChannelConnector();
        connector.setPort(port);
        setConnectors(new Connector[]{connector});
    }

    protected void doStart() throws Exception
    {
        if (_threadPool==null)
            setThreadPool(new QueuedThreadPool());
        try {
            super.doStart();
        }
        catch(Throwable e) {
            mex.add(e);
        }

        if (_connectors!=null && mex.size()==0) {
            for (int i=0;i<_connectors.length;i++) {
                try{_connectors[i].start();}
                catch(Throwable e)
                {
                    mex.add(e);
                }
            }
        }
     
    }
}

Server中包含的两个重要的成员是_threadPool和_connectors。_threadPool为整个Jetty提供统一的线程池服务,所有实现了Runnable接口的可执行任务都通过该线程池统一调度。_connectors是包含Connector的数组(废话),你可以将一个Connector理解为对于一个ServerSocket或者ServerSocketChannel的封装。

Server在初始化并启动时,最重要的两项操作是创建一个SelectChannelConnector(默认只创建一个connector)和一个类型为QueuedThreadPool的线程池。有了connector就可以监听并接受Http请求;有了线程池就可以采用多线程来处理接收到Http请求。

Jetty的NIO包含下列核心组件:

1)Connector(组装器)

2)Acceptor(独立子线程)

3)SelectorManager(独立子线程)

4)SelectorSet(反应堆模式中的multiplexer)

7)QueuedThreadPool(独立线程池)

5)EndPoint(反应堆模式中的dispatcher)

6)Connection(反应堆模式中的handler)

Acceptor负责循环接收新请求,并交给多路复用器multiplexer进行监控读写事件,有事件发生后,multiplexer将通道交给分拣器dispatcher负责处理,dispatcher将通道通过线程池交给Connection处理。

Acceptor与multiplexer的交互,multiplexer与handler的交互,都是通过等待队列实现,即都是拉的模式。(proactor模式) Acceptor与multiplexer交互的等待队列是SelectSet中的_changes,类型为ConcurrentLinkedQueue<Object>;multiplexer与handler的交互的等待队列实现在QueuedThreadPool中的BlockingQueue<Runnable> _jobs。

以上组件的交互序列图为:


Connector中也包含了两个重要的成员,_acceptChannel和_manager。_acceptChannel很容易理解就是用来监听客户端Http请求的服务端Socket;而_manager的类型是SelectorManager,见名知意,应该是Java NIO中的Selector的管理器,这个后面再详细来看。

Connector启动的大部分工作主要集中在其父类的父类AbstractConnector的doStart()方法中,其中主要干了这么两件事:

1. 初始化ServerSocketChannel并在指定的端口上监听客户端请求。

2. 创建指定数目的Acceptor并将Acceptor挂接到Server的线程池中的某个空闲线程上。

public class SelectChannelConnector extends AbstractNIOConnector {
    protected ServerSocketChannel _acceptChannel;
    private final SelectorManager _manager = new ConnectorSelectorManager();

    public void open() throws IOException
    {
        synchronized(this)
        {
            if (_acceptChannel == null)
            {
                // Create a new server socket
                _acceptChannel = ServerSocketChannel.open();
                // Set to blocking mode
                _acceptChannel.configureBlocking(true);

                // Bind the server socket to the local host and port
                _acceptChannel.socket().setReuseAddress(getReuseAddress());
                InetSocketAddress addr = getHost()==null?new InetSocketAddress(getPort()):new InetSocketAddress(getHost(),getPort());
                _acceptChannel.socket().bind(addr,getAcceptQueueSize());

                _localPort=_acceptChannel.socket().getLocalPort();
                if (_localPort<=0)
                    throw new IOException("Server channel not bound");

                addBean(_acceptChannel);
            }
        }
    }

    protected void doStart() throws Exception
    {
        _manager.setSelectSets(getAcceptors());
        _manager.setMaxIdleTime(getMaxIdleTime());
        _manager.setLowResourcesConnections(getLowResourcesConnections());
        _manager.setLowResourcesMaxIdleTime(getLowResourcesMaxIdleTime());

        super.doStart();
    }
}

public abstract class AbstractConnector extends AggregateLifeCycle implements HttpBuffers, Connector, Dumpable {
    protected void doStart() throws Exception
    {
        if (_server == null)
            throw new IllegalStateException("No server");

        // open listener port
        open();

        if (_threadPool == null)
        {
            _threadPool = _server.getThreadPool();
            addBean(_threadPool,false);
        }

        super.doStart();

        // Start selector thread
        synchronized (this)
        {
            _acceptorThreads = new Thread[getAcceptors()];

            for (int i = 0; i < _acceptorThreads.length; i++)
                if (!_threadPool.dispatch(new Acceptor(i)))
                    throw new IllegalStateException("!accepting");
            if (_threadPool.isLowOnThreads())
                LOG.warn("insufficient threads configured for {}",this);
        }
    }
}

Acceptor实现上为Connector的一个内部类,并且实现了Runnable接口,Acceptor的作用更像是Connector的分身,打个不恰当的比喻,Connector就像是孙悟空,而其包含的多个Acceptor就像是其一根猴毛变出来的众多猴子猴孙,一到遇到妖精,悟空就派出徒子徒孙去迎战。从下面代码Acceptor所执行的任务也可以看出,一旦Connector创建出了一个Acceptor,就将该Acceptor交付给Server中的线程池等待调度执行,而获得某一空闲线程而被执行,Acceptor就是无限循环的调用Connector的accept方法尝试接受客户端Http请求,直到被显示停止。因此可以说,Acceptor是Connector的一个分身,是替Connector工作,从某种角度讲,Connector是静态的,Acceptor是动态的。

public abstract class AbstractConnector extends AggregateLifeCycle implements HttpBuffers, Connector, Dumpable {
    private class Acceptor implements Runnable
    {
        int _acceptor = 0;

        Acceptor(int id)
        {
            _acceptor = id;
        }

        /* ------------------------------------------------------------ */
        public void run()
        {
            Thread current = Thread.currentThread();
            String name;
            synchronized (AbstractConnector.this)
            {
                if (_acceptorThreads == null)
                    return;

                _acceptorThreads[_acceptor] = current;
                name = _acceptorThreads[_acceptor].getName();
                current.setName(name + " Acceptor" + _acceptor + " " + AbstractConnector.this);
            }
            int old_priority = current.getPriority();

            try
            {
                current.setPriority(old_priority - _acceptorPriorityOffset);
                while (isRunning() && getConnection() != null)
                {
                    try
                    {
                        accept(_acceptor);
                    }
                    catch (EofException e)
                    {
                        LOG.ignore(e);
                    }
                    catch (IOException e)
                    {
                        LOG.ignore(e);
                    }
                    catch (InterruptedException x)
                    {
                        // Connector has been stopped
                        LOG.ignore(x);
                    }
                    catch (Throwable e)
                    {
                        LOG.warn(e);
                    }
                }
            }
            finally
            {
                current.setPriority(old_priority);
                current.setName(name);

                synchronized (AbstractConnector.this)
                {
                    if (_acceptorThreads != null)
                        _acceptorThreads[_acceptor] = null;
                }
            }
        }
    }
}

Acceptor在接收到用户请求后,对socket进行一些配置,然后将channel注册到SelectorManager中,此后便开始了Jetty的NIO过程。至此就是Acceptor的一个完整工作流程,在成功将客户端接收到的socketChannel交付给selector后,就开始了下一次接收操作,不断的接收,交付,接收,交付,Acceptor就是如此不辞辛劳的干着接客的生意。

public class SelectChannelConnector extends AbstractNIOConnector {
    public void accept(int acceptorID) throws IOException
    {
        ServerSocketChannel server;
        synchronized(this)
        {
            server = _acceptChannel;
        }

        if (server!=null && server.isOpen() && _manager.isStarted())
        {
            SocketChannel channel = server.accept();
            channel.configureBlocking(false);
            Socket socket = channel.socket();
            configure(socket);
            _manager.register(channel);
        }
    }
}

public abstract class SelectorManager extends AbstractLifeCycle implements Dumpable {
    private SelectSet[] _selectSet;

    public void register(SocketChannel channel)
    {
        // The ++ increment here is not atomic, but it does not matter.
        // so long as the value changes sometimes, then connections will
        // be distributed over the available sets.

        int s=_set++;
        if (s<0)
            s=-s;
        s=s%_selectSets;
        SelectSet[] sets=_selectSet;
        if (sets!=null)
        {
            SelectSet set=sets[s];
            set.addChange(channel);
            set.wakeup();
        }
    }
}

在开始Jetty的NIO过程前,先来看看Jetty默认的线程池QueuedThreadPool

public class QueuedThreadPool extends AbstractLifeCycle implements SizedThreadPool, Executor, Dumpable {

    // 当前已经启动的线程,这些线程不断循环从_jobs中获取并运行任务,如果没有任务,就阻塞等待
    private final ConcurrentLinkedQueue<Thread> _threads=new ConcurrentLinkedQueue<Thread>();

    // 已经在排队,但尚未获得运行的任务
    private BlockingQueue<Runnable> _jobs;

    // 已经运行的线程数
    private final AtomicInteger _threadsStarted = new AtomicInteger();

    // 空闲的线程数
    private final AtomicInteger _threadsIdle = new AtomicInteger();

    // 将任务加入任务队列,如果有空闲任务,且仍可以启动新线程,则添加一个新线程执行任务
    public boolean dispatch(Runnable job)
    {
        if (isRunning())
        {
            final int jobQ = _jobs.size();
            final int idle = getIdleThreads();
            if(_jobs.offer(job))
            {
                // If we had no idle threads or the jobQ is greater than the idle threads
                if (idle==0 || jobQ>idle)
                {
                    int threads=_threadsStarted.get();
                    if (threads<_maxThreads)
                        startThread(threads);
                }
                return true;
            }
        }
        LOG.debug("Dispatched {} to stopped {}",job,this);
        return false;
    }

    // 线程池启动的时候,启动最小数量的线程
    protected void doStart() throws Exception
    {
        super.doStart();
        _threadsStarted.set(0);

        if (_jobs==null)
        {
            _jobs=_maxQueued>0 ?new ArrayBlockingQueue<Runnable>(_maxQueued)
                :new BlockingArrayQueue<Runnable>(_minThreads,_minThreads);
        }

        int threads=_threadsStarted.get();
        while (isRunning() && threads<_minThreads)
        {
            startThread(threads);
            threads=_threadsStarted.get();
        }
    }

    // 线程池中运行的线程,这些线程不断从任务队列中获取并执行任务,如果设置了线程的最大空闲时间,则适当缩小线程池中的线程数量
    private Runnable _runnable = new Runnable()
    {
        public void run()
        {
            boolean shrink=false;
            try
            {
                Runnable job=_jobs.poll();
                while (isRunning())
                {
                    // Job loop
                    while (job!=null && isRunning())
                    {
                        runJob(job);
                        job=_jobs.poll();
                    }

                    // Idle loop
                    try
                    {
                        _threadsIdle.incrementAndGet();

                        while (isRunning() && job==null)
                        {
                            if (_maxIdleTimeMs<=0)
                                job=_jobs.take();
                            else
                            {
                                // maybe we should shrink?
                                final int size=_threadsStarted.get();
                                if (size>_minThreads)
                                {
                                    long last=_lastShrink.get();
                                    long now=System.currentTimeMillis();
                                    if (last==0 || (now-last)>_maxIdleTimeMs)
                                    {
                                        shrink=_lastShrink.compareAndSet(last,now) &&
                                        _threadsStarted.compareAndSet(size,size-1);
                                        if (shrink)
                                            return;
                                    }
                                }
                                job=idleJobPoll();
                            }
                        }
                    }
                    finally
                    {
                        _threadsIdle.decrementAndGet();
                    }
                }
            }
            catch(InterruptedException e)
            {
                LOG.ignore(e);
            }
            catch(Exception e)
            {
                LOG.warn(e);
            }
            finally
            {
                if (!shrink)
                    _threadsStarted.decrementAndGet();
                _threads.remove(Thread.currentThread());
            }
        }
    };

    protected void runJob(Runnable job)
    {
        job.run();
    }
}

了解清楚了Server, Connector, Acceptor, QueuedThreadPool后,接下来可以研究一下Jetty的NIO了。

Acceptor接收了客户端的Http请求后,将SocketChannel注册到了SelectorManager,说到SelectorManager,就要先了解一个SelectorManager重要的内部类SelectorSet. 不要被他的名字所迷惑,他并不是Selector的集合,SelectorSet简单来说是对一个Selector的封装,封装的目的是为该Selector添加了一个等待队列,所有希望被该Selector监控的通道都加入这个等待队列中等待被调度。

SelectorManager中包含了一个SelectorSet的集合_selectSet,也就是说可以为Jetty配置多个Selector用于监控通道。在Selector启动的时候,首先初始化配置文件中指定个数的SelectorSet,然后逐个启动所有的SelectorSet,让其中的Selector开始不断循环监控注册在其上的通道。启动SelectorSet的方式是调用SelectManager的dispatch方法,该方法在SelectorManager中被声明为抽象方法,之所以没有在此实现该方法是因为执行循环调用SelectSet.doSelect()方法需要一个线程,而线程的调度需要线程池的配合,此处没有实现dispatch方法就是不想和具体的线程池实现相绑定,是一种松耦合的设计,将专业的工作交给专业的人去做。

public abstract class SelectorManager extends AbstractLifeCycle implements Dumpable {
     private SelectSet[] _selectSet;															
    // SelectorManager初始启动时,其将所有的SelectSet加入到线程池的任务队列中等待运行,而SelectorSet的任务就是不停的执行Select.                           
    protected void doStart() throws Exception
    {
        _selectSet = new SelectSet[_selectSets];
        for (int i=0;i<_selectSet.length;i++)
            _selectSet[i]= new SelectSet(i);

        super.doStart();

        // start a thread to Select
        for (int i=0;i<getSelectSets();i++)
        {
            final int id=i;
            boolean selecting=dispatch(new Runnable()
            {
                public void run()
                {
                    String name=Thread.currentThread().getName();
                    int priority=Thread.currentThread().getPriority();
                    try
                    {
                        SelectSet[] sets=_selectSet;
                        if (sets==null)
                            return;
                        SelectSet set=sets[id];

                        Thread.currentThread().setName(name+" Selector"+id);
                        if (getSelectorPriorityDelta()!=0)
                            Thread.currentThread().setPriority(Thread.currentThread().getPriority()+getSelectorPriorityDelta());
                        LOG.debug("Starting {} on {}",Thread.currentThread(),this);
                        while (isRunning())
                        {
                            try
                            {
                                set.doSelect();
                            }
                            catch(IOException e)
                            {
                                LOG.ignore(e);
                            }
                            catch(Exception e)
                            {
                                LOG.warn(e);
                            }
                        }
                    }
                    finally
                    {
                        LOG.debug("Stopped {} on {}",Thread.currentThread(),this);
                        Thread.currentThread().setName(name);
                        if (getSelectorPriorityDelta()!=0)
                            Thread.currentThread().setPriority(priority);
                    }
                }

            });

            if (!selecting)
                throw new IllegalStateException("!Selecting");
        }
    }

    // 看看SelectSet是如何执行NIO的Select操作
    public class SelectSet implements Dumpable
    {
        // 等待加入到Selector中的对象,由于泛型的类型是Objcct, 这点很重要,就是可以加入任意类型的对象。(这种设计是否合理呢?)
        private final ConcurrentLinkedQueue<Object> _changes = new ConcurrentLinkedQueue<Object>();

	// 当前SelectSet所使用的Selector
        private volatile Selector _selector;

	// 当前运行该selector的线程
        private volatile Thread _selecting;

        private ConcurrentMap<SelectChannelEndPoint,Object> _endPoints = new ConcurrentHashMap<SelectChannelEndPoint, Object>();

	// 构造函数做的工作很简单,就是创建一个新的Selector
        SelectSet(int acceptorID) throws Exception
        {
            // create a selector;
            _selector = Selector.open();
        }

	// 重点来了,Jetty的NIO就在这里实现
	/* ------------------------------------------------------------ */
        /**
         * Select and dispatch tasks found from changes and the selector.
         *
         * @throws IOException
         */
        public void doSelect() throws IOException
        {
            try
            {
                _selecting=Thread.currentThread();
                final Selector selector=_selector;
                // Stopped concurrently ?
                if (selector == null)
                    return;

                // Make any key changes required
                Object change;
                int changes=_changes.size();
                while (changes-->0 && (change=_changes.poll())!=null)
                {
                    Channel ch=null;
                    SelectionKey key=null;

                    try
                    {
                        if (change instanceof EndPoint)
                        {
                            // Update the operations for a key.
                            SelectChannelEndPoint endpoint = (SelectChannelEndPoint)change;
                            ch=endpoint.getChannel();
                            endpoint.doUpdateKey();
                        }
                        else if (change instanceof ChannelAndAttachment)
                        {
                            // finish accepting/connecting this connection
                            final ChannelAndAttachment asc = (ChannelAndAttachment)change;
                            final SelectableChannel channel=asc._channel;
                            ch=channel;
                            final Object att = asc._attachment;

                            if ((channel instanceof SocketChannel) && ((SocketChannel)channel).isConnected())
                            {
                                key = channel.register(selector,SelectionKey.OP_READ,att);
                                SelectChannelEndPoint endpoint = createEndPoint((SocketChannel)channel,key);
                                key.attach(endpoint);
                                endpoint.schedule();
                            }
                            else if (channel.isOpen())
                            {
                                key = channel.register(selector,SelectionKey.OP_CONNECT,att);
                            }
                        }
                        else if (change instanceof SocketChannel)
                        {
                            // Newly registered channel
                            final SocketChannel channel=(SocketChannel)change;
                            ch=channel;
                            key = channel.register(selector,SelectionKey.OP_READ,null);
                            SelectChannelEndPoint endpoint = createEndPoint(channel,key);
                            key.attach(endpoint);
                            endpoint.schedule();
                        }
                        else if (change instanceof ChangeTask)
                        {
                            ((Runnable)change).run();
                        }
                        else if (change instanceof Runnable)
                        {
                            dispatch((Runnable)change);
                        }
                        else
                            throw new IllegalArgumentException(change.toString());
                    }
                    catch (CancelledKeyException e)
                    {
                    }
                    catch (Throwable e)
                    {
                    }
                }


                // Do and instant select to see if any connections can be handled.
                int selected=selector.selectNow();

                long now=System.currentTimeMillis();

                // if no immediate things to do
                if (selected==0 && selector.selectedKeys().isEmpty())
                {
                    // If we are in pausing mode
                    if (_pausing)
                    {
                        try
                        {
                            Thread.sleep(__BUSY_PAUSE); // pause to reduce impact of  busy loop
                        }
                        catch(InterruptedException e)
                        {
                            LOG.ignore(e);
                        }
                        now=System.currentTimeMillis();
                    }

                    // workout how long to wait in select
                    _timeout.setNow(now);
                    long to_next_timeout=_timeout.getTimeToNext();

                    long wait = _changes.size()==0?__IDLE_TICK:0L;
                    if (wait > 0 && to_next_timeout >= 0 && wait > to_next_timeout)
                        wait = to_next_timeout;

                    // If we should wait with a select
                    if (wait>0)
                    {
                        long before=now;
                        selector.select(wait);
                        now = System.currentTimeMillis();
                        _timeout.setNow(now);

                        // If we are monitoring for busy selector
                        // and this select did not wait more than 1ms
                        if (__MONITOR_PERIOD>0 && now-before <=1)
                        {
                            // count this as a busy select and if there have been too many this monitor cycle
                            if (++_busySelects>__MAX_SELECTS)
                            {
                                // Start injecting pauses
                                _pausing=true;

                                // if this is the first pause
                                if (!_paused)
                                {
                                    // Log and dump some status
                                    _paused=true;
                                    LOG.warn("Selector {} is too busy, pausing!",this);
                                }
                            }
                        }
                    }
                }

                // have we been destroyed while sleeping
                if (_selector==null || !selector.isOpen())
                    return;

                // Look for things to do
                for (SelectionKey key: selector.selectedKeys())
                {
                    SocketChannel channel=null;

                    try
                    {
                        if (!key.isValid())
                        {
                            key.cancel();
                            SelectChannelEndPoint endpoint = (SelectChannelEndPoint)key.attachment();
                            if (endpoint != null)
                                endpoint.doUpdateKey();
                            continue;
                        }

                        Object att = key.attachment();
                        if (att instanceof SelectChannelEndPoint)
                        {
                            if (key.isReadable()||key.isWritable())
                                ((SelectChannelEndPoint)att).schedule();
                        }
                        else if (key.isConnectable())
                        {
                            // Complete a connection of a registered channel
                            channel = (SocketChannel)key.channel();
                            boolean connected=false;
                            try
                            {
                                connected=channel.finishConnect();
                            }
                            catch(Exception e)
                            {
                                connectionFailed(channel,e,att);
                            }
                            finally
                            {
                                if (connected)
                                {
                                    key.interestOps(SelectionKey.OP_READ);
                                    SelectChannelEndPoint endpoint = createEndPoint(channel,key);
                                    key.attach(endpoint);
                                    endpoint.schedule();
                                }
                                else
                                {
                                    key.cancel();
                                }
                            }
                        }
                        else
                        {
                            // Wrap readable registered channel in an endpoint
                            channel = (SocketChannel)key.channel();
                            SelectChannelEndPoint endpoint = createEndPoint(channel,key);
                            key.attach(endpoint);
                            if (key.isReadable())
                                endpoint.schedule();
                        }
                        key = null;
                    }
                    catch (CancelledKeyException e)
                    {
                        LOG.ignore(e);
                    }
                    catch (Exception e)
                    {
                        if (isRunning())
                            LOG.warn(e);
                        else
                            LOG.ignore(e);

                        try
                        {
                            if (channel!=null)
                                channel.close();
                        }
                        catch(IOException e2)
                        {
                            LOG.debug(e2);
                        }

                        if (key != null && !(key.channel() instanceof ServerSocketChannel) && key.isValid())
                            key.cancel();
                    }
                }

                // Everything always handled
                selector.selectedKeys().clear();

                now=System.currentTimeMillis();
                _timeout.setNow(now);
                Task task = _timeout.expired();
                while (task!=null)
                {
                    if (task instanceof Runnable)
                        dispatch((Runnable)task);
                    task = _timeout.expired();
                }

                // Idle tick
                if (now-_idleTick>__IDLE_TICK)
                {
                    _idleTick=now;

                    final long idle_now=((_lowResourcesConnections>0 && selector.keys().size()>_lowResourcesConnections))
                        ?(now+_maxIdleTime-_lowResourcesMaxIdleTime)
                        :now;

                    dispatch(new Runnable()
                    {
                        public void run()
                        {
                            for (SelectChannelEndPoint endp:_endPoints.keySet())
                            {
                                endp.checkIdleTimestamp(idle_now);
                            }
                        }
                        public String toString() {return "Idle-"+super.toString();}
                    });

                }

                // Reset busy select monitor counts
                if (__MONITOR_PERIOD>0 && now>_monitorNext)
                {
                    _busySelects=0;
                    _pausing=false;
                    _monitorNext=now+__MONITOR_PERIOD;

                }
            }
            catch (ClosedSelectorException e)
            {
                if (isRunning())
                    LOG.warn(e);
                else
                    LOG.ignore(e);
            }
            catch (CancelledKeyException e)
            {
                LOG.ignore(e);
            }
            finally
            {
                _selecting=null;
            }
        }
}

上述方法就是Jetty的NIO核心操作,整个流程非常复杂,混合了BIO, NIO等多种IO模式,能写出这段代码俺是真心佩服。。。

由于SelectorSet的成员_changes声明为ConcurrentLinkedQueue<Object>类型,因此可以向该等待队列中添加任意类型的对象,SelectorSet就是通过判断对象的类型来执行不同的IO模式。

doSelect函数概括起来共包含四部分工作:
1) 处理所有包含在_changes中等待加入Selector轮询的对象,根据对象类型的不同,具体处理方式不同,这里稍后分析。
2) 执行无阻塞selector.selectNow操作, 查询有无注册事件发生,然后立即返回。

3) 如果此时没有事件发生,并且配置了无事件时需要暂停休息,则睡眠一段时间,让出CPU。睡眠结束后,如果_changes队列不为空,也就是说还有通道等待轮询,则再执行一次阻塞select调用,阻塞的时间会提前计算好,Jetty有自己实现的一个计时器Timeout。

4) 如果经过上述步骤后有事件发生,则依次对每一个事件进行,处理的方式根据不同的事件而不同。



既然Selector操作的对象均是来自_changes,那要来看看_changes是在什么地方被填充,_changes的添加被封装在SelectorManager的addChange方法中:

 public void addChange(Object change)
{
     _changes.add(change);
}

而调用addChange的方法的地方如下几处:

1) 该方法用于SelectorConnector,该Selector用于BIO,暂时在此不讨论。  

public class SelectorManager {																							       public void addChange(SelectableChannel channel, Object att)
    {
        if (att==null)
	    addChange(channel);
        else if (att instanceof EndPoint)
	    addChange(att);
        else
	    addChange(new ChannelAndAttachment(channel,att));
    }
}
2)

public class SelectorManager {																										public void register(ServerSocketChannel acceptChannel)
 { int s=_set++; 
if (s<0) s=-s; 
s=s%_selectSets; 
SelectSet set=_selectSet[s]; 
set.addChange(acceptChannel); 
set.wakeup(); 
}}

3) 该方法被SelectChannelConnector的accept方法调用,为Connector与SelectorSet的交流接口,即Acceptor在接收了用户的Http请求后,将接收到的SocketChannel加入到SelectorSet的_changes中,等待被Selector轮询。此时向_changes中加入的Object对象的真实类型是SelectChannel,对应SelectSet.doSelect方法第一部分的第三个if-else分支,即创建新的SelectChannelEndPoint。

public class SelectorManager {
public void register(SocketChannel channel)
 { 
    int s=_set++; 
    if (s<0) s=-s; 
    s=s%_selectSets; 
    SelectSet[] sets=_selectSet; 
    if (sets!=null) { 
        SelectSet set=sets[s]; 
        set.addChange(channel); 
        set.wakeup(); 
    } 
}}

4) 如果感兴趣的事件发生了变化,需要将自己告诉给Selector,重新完成注册。此时向_changes中加入的Object对象的真实类型是SelectChannelEndPoint,作用是更新自己,对应SelectSet.doSelect方法第一部分的第一个if-else分支。

public class SelectorManager {
private void updateKey()
    {
        final boolean changed;
        synchronized (this)
        {
            int current_ops=-1;
            if (getChannel().isOpen())
            {
                boolean read_interest = _readBlocked || (!_dispatched && !_connection.isSuspended());
                boolean write_interest= _writeBlocked || (!_dispatched && !_writable);

                _interestOps =
                    ((!_socket.isInputShutdown() && read_interest ) ? SelectionKey.OP_READ  : 0)
                |   ((!_socket.isOutputShutdown()&& write_interest) ? SelectionKey.OP_WRITE : 0);
                try
                {
                    current_ops = ((_key!=null && _key.isValid())?_key.interestOps():-1);
                }
                catch(Exception e)
                {
                    _key=null;
                    LOG.ignore(e);
                }
            }
            changed=_interestOps!=current_ops;
        }

        if(changed)
        {
            _selectSet.addChange(this);
            _selectSet.wakeup();
        }
    }

5) 该方法是在唤醒Selector出现异常的时候,向_changes中添加更换Selector的任务。此时向_changes中加入的Object对象的真实类型是ChangeTask,对应SelectSet.doSelect方法第一部分的第四个if-else分支。

public class SelectorManager {
public void wakeup()
 { try { Selector selector = _selector; if (selector!=null) selector.wakeup(); } catch(Exception e) { addChange(new ChangeTask() { public void run() { renewSelector(); } }); renewSelector(); } }}

6) SelectSet.doSelect方法第一部分的第二个if-else分支中加入_changes中的Object对象的真实类型是ChannelAndAttachment,该类型应用的场景是SelectConnector所对应的BIO,在此暂时不讨论。

在SelectSet的doSelect方法的各个步骤中,我们多次看到对于endpoint.schedule()方法的调用,该方法的实现如下:

public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPoint, ConnectedEndPoint {
    public void schedule()
    {
        synchronized (this)
        {
            // If there is no key, then do nothing
            if (_key == null || !_key.isValid())
            {
                _readBlocked=false;
                _writeBlocked=false;
                this.notifyAll();
                return;
            }

            // If there are threads dispatched reading and writing
            if (_readBlocked || _writeBlocked)
            {
                // assert _dispatched;
                if (_readBlocked && _key.isReadable())
                    _readBlocked=false;
                if (_writeBlocked && _key.isWritable())
                    _writeBlocked=false;

                // wake them up is as good as a dispatched.
                this.notifyAll();

                // we are not interested in further selecting
                _key.interestOps(0);
                if (!_dispatched)
                    updateKey();
                return;
            }

            // Remove writeable op
            if ((_key.readyOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE && (_key.interestOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE)
            {
                // Remove writeable op
                _interestOps = _key.interestOps() & ~SelectionKey.OP_WRITE;
                _key.interestOps(_interestOps);
                _writable = true; // Once writable is in ops, only removed with dispatch.
            }

            // If dispatched, then deregister interest
            if (_dispatched)
                _key.interestOps(0);
            else
            {
                // other wise do the dispatch
                dispatch();
                if (_dispatched && !_selectSet.getManager().isDeferringInterestedOps0())
                {
                    _key.interestOps(0);
                }
            }
        }
    }
}

该方法又引出了另外两个重要的方法:SelectChannelEndPoint.dispatch() & SelectChannelEndPoint.handle()

public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPoint, ConnectedEndPoint {
    public void dispatch()
    {
        synchronized(this)
        {
            if (!_dispatched)
            {
                _dispatched = true;
                boolean dispatched = _manager.dispatch(_handler);
                if(!dispatched)
                {
                    _dispatched = false;
                    LOG.warn("Dispatched Failed! "+this+" to "+_manager);
                    updateKey();
                }
            }
        }
    }
}

dispatch要做的事情就是通知线程池,该EndPoint所对应的Http请求是时候可以处理了。而其实现的具体方式是,如果该EndPoint还没有丢进线程池,就通过_manager将EndPoint中的handler任务加入线程池等待调度,handler是一个Runnable的实现类,实现了具体的处理Http请求的业务逻辑。至此,Jetty的NIO就分析完了,总结下来就是一个Reactor模式,一个MainReactor(Acceptor)和一个subReactor(SelectorSet), 一个负责生成代表客户端Http请求的EndPoint,一个负责处理EndPoint, 至于为什么需要包装一个EndPoint类,那是因为需要在其上添加一些额外的校验功能,例如checkIdle等等。

下面来看看具体的Http请求处理业务逻辑。
public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPoint, ConnectedEndPoint {
    protected void handle()
    {
        boolean dispatched=true;
        try
        {
            while(dispatched)
            {
                try
                {
                    while(true)
                    {
                        final AsyncConnection next = (AsyncConnection)_connection.handle();
                        if (next!=_connection)
                        {
                            LOG.debug("{} replaced {}",next,_connection);
                            Connection old=_connection;
                            _connection=next;
                            _manager.endPointUpgraded(this,old);
                            continue;
                        }
                        break;
                    }
                }
                catch (Throwable e)
                {
                    LOG.warn("handle failed", e);
                    try{close();}
                    catch(IOException e2){LOG.ignore(e2);}
                }
                finally
                {
                    if (!_ishut && isInputShutdown() && isOpen())
                    {
                        _ishut=true;
                        try
                        {
                            _connection.onInputShutdown();
                        }
                        catch(Throwable x)
                        {
                            LOG.warn("onInputShutdown failed", x);
                            try{close();}
                            catch(IOException e2){LOG.ignore(e2);}
                        }
                        finally
                        {
                            updateKey();
                        }
                    }
                    dispatched=!undispatch();
                }
            }
        }
        finally
        {
            if (dispatched)
            {
                dispatched=!undispatch();
                while (dispatched)
                {
                    LOG.warn("SCEP.run() finally DISPATCHED");
                    dispatched=!undispatch();
                }
            }
        }
    }
}
_connection.handle()是真正的Http请求处理的开始。




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值