Jetty源碼筆記

Jetty源碼筆記
1. Jetty核心抽象
2. SelectChannelConnector分析
2.1. 基本模型與流程
2.1.1. Acceptor
2.1.2. Reactor
2.1.3. Select的工作流程
1. Jetty核心抽象
Connector: 接受client連接,構造Connection並handle connection的組件。
Handler: 處理request併產生response。
Server: Connector和Handler的容器,它通過配置或者編程的方式組裝Connector和Handler集合,並管理它們的聲明週期。同時server還是線程池的管理者,向Connecotr和Handler提供線程池的服務。
EndPoint: 對server的通訊目標的抽象,其中封裝了讀、寫等IO操作。
Buffer: 顧名思義,Buffer是Jetty統一緩存接口所做的抽象,EndPoint的讀寫方法都接受Buffer作爲參數。
Connection: Connection是對連接的抽象,核心方法是handle,供Connector調用、處理連接。Connection的核心實現是HttpConnection,
它封裝了Request、Response,並將其關聯到EndPoint上。


2. SelectChannelConnector分析
2.1. 基本模型與流程
2.1.1. Acceptor
/* ------------------------------------------------------------ */
publicvoidopen() throwsIOException
{
    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?
                                 newInetSocketAddress(getPort())
                                :newInetSocketAddress(getHost(),getPort());
            _acceptChannel.socket().bind(addr,getAcceptQueueSize());
 
            _localPort=_acceptChannel.socket().getLocalPort();
            if(_localPort<=0)
                thrownewIOException("Server channel not bound");
 
        }
    }
}


一個SelectChannelConnector會啓動一個獨立的線程用來accept連接,要注意的是,雖然它是基於NIO的,但accept仍然採用阻塞模式。 這個線程不斷阻塞調用server.accept()來接受連接並想SelectorManager裏註冊(新建立的SocketChannel則被配置爲非阻塞模式)。


Acceptor線程的內部邏輯,循環調用accept來接受連接,並將連接Channel註冊到SelectorManager裏:
// start a thread to accept new connections
_manager.dispatch(newRunnable()
{
    publicvoidrun()
    {
        finalServerSocketChannel server=_acceptChannel;
        while(isRunning() && _acceptChannel==server && server.isOpen())
        {
            try
            {
                SocketChannel channel = server.accept();
                channel.configureBlocking(false);
                Socket socket = channel.socket();
                configure(socket);
                _manager.register(channel);
            }
            catch(IOException e)
            {
                Log.ignore(e);
            }
        }
    }
});


2.1.2. Reactor
在父類AbstractConnector中啓動的N個acceptor線程會循環調用子類實現的accept方法,但是在SelectChannelConnector裏, 這個accept方法裏做的是調用SelectorManager的doSelect,而非accept,所以這裏將其命名爲Reactor。


AbstractConnector裏啓動了多個acceptor線程:

// Start selector thread
synchronized(this)
{
    _acceptorThread = newThread[getAcceptors()];
 
    for(inti = 0; i < _acceptorThread.length; i++)
        _threadPool.dispatch(newAcceptor(i));
    if(_threadPool.isLowOnThreads())
        Log.warn("insufficient threads configured for {}",this);
}

Acceptor裏的邏輯就是簡單調用AbstractConnector中的抽象accept方法:
publicvoidrun()
{
    /* 省略... */
    try
    {
        current.setPriority(old_priority - _acceptorPriorityOffset);
        while (isRunning() && getConnection() != null)
        {
            try
            {
                accept(_acceptor);
            }
            catch (...) { /* 省略... */ }
        }
    }
    finally { /* 省略... */ }
}

SelectChannelConnector裏的accept,實際上做的根本就不是accept連接的動作,相反,它調用了SelectorManager的doSelect方法。
/* ------------------------------------------------------------ */
@Override
publicvoidaccept(intacceptorID)throwsIOException
{
    _manager.doSelect(acceptorID);
}

SelectorManager的doSelect也僅僅是將調用委託給了acceptorID對應的SelectSet的doSelect方法。SelectManager裏的SelectSet是 一個數組,默認只有一個元素,但可以更改。在SelectChannelConnector裏,其數目和acceptor一致。


2.1.3. Select的工作流程
SelectSet的doSelect是核心,它主要完成了三大塊工作:


處理change queue裏的changes。change可能是EndPoint, ChannelAndAttachment, SocketChannel或者Runnalble,這裏會做對應的 處理。比如acceptor接受了一個新連接以後調用的register方法就會往change queue里加入對應的SocketChannel對象,在這裏,此對象 就會被包裝成SocketChannelEndPoint,並將其註冊到selector裏。
調用selectNow,如果沒有select到事件,可能會進一步調用select,阻塞一段時間。如果有事件,就拿到SelectionKey的attachment, 即EndPoint,做schedule和dispatch。
處理idle tick相關邏輯,此處不關心(TODO:瞭解這個設計的用途)
?
/* ------------------------------------------------------------ */
/**
 * 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;
 
        // Make any key changes required
        Object change;
        int changes=_changes.size();
        while (changes-->0 && (change=_changes.poll())!=null)
        {
            try
            {
                if (change instanceof EndPoint)
                {
                    // Update the operations for a key.
                    SelectChannelEndPoint endpoint = (SelectChannelEndPoint)change;
                    endpoint.doUpdateKey();
                }
                else if (change instanceof ChannelAndAttachment)
                {
                    // finish accepting/connecting this connection
                    final ChannelAndAttachment asc = (ChannelAndAttachment)change;
                    final SelectableChannel channel=asc._channel;
                    final Object att = asc._attachment;
                    SelectionKey key = channel.register(selector,SelectionKey.OP_READ,att);
                    SelectChannelEndPoint endpoint = createEndPoint((SocketChannel)channel,key);
                    key.attach(endpoint);
                    endpoint.schedule();
                }
                else if (change instanceof SocketChannel)
                {
                    // Newly registered channel
                    final SocketChannel channel=(SocketChannel)change;
                    SelectionKey key = channel.register(selector,SelectionKey.OP_READ,null);
                    SelectChannelEndPoint endpoint = createEndPoint(channel,key);
                    key.attach(endpoint);
                    endpoint.schedule();
                }
                else if (change instanceof Runnable)
                {
                    dispatch((Runnable)change);
                }
                else
                    throw new IllegalArgumentException(change.toString());
            }
            catch (Exception e)
            {
                if (isRunning())
                    Log.warn(e);
                else
                    Log.debug(e);
            }
            catch (Error e)
            {
                if (isRunning())
                    Log.warn(e);
                else
                    Log.debug(e);
            }
        }
 
 
        // Do and instant select to see if any connections can be handled.
        int selected=selector.selectNow();
        _selects++;
 
        long now=System.currentTimeMillis();
         
        // if no immediate things to do
        if (selected==0)
        {
            // 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;
                selected=selector.select(wait);
                _selects++;
                now = System.currentTimeMillis();
                _timeout.setNow(now);
                 
                if (__JVMBUG_THRESHHOLD>0)
                    checkJvmBugs(before, now, wait, selected);
            }
        }
         
        // have we been destroyed while sleeping
        if (_selector==null || !selector.isOpen())
            return;
 
        // Look for things to do
        for (SelectionKey key: selector.selectedKeys())
        {  
            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)
                {
                    ((SelectChannelEndPoint)att).schedule();
                }
                else
                {
                    // Wrap readable registered channel in an endpoint
                    SocketChannel 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);
 
                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
        /* ... */
        / * We do not care this here. Refer to the source code */
    }
    catch(CancelledKeyException e)
    {
        Log.ignore(e);
    }
    finally
    {
        _selecting=null;
    }
}

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章