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
/* ------------------------------------------------------------ */
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" );
 
         }
     }
}


一个SelectChannelConnector会启动一个独立的线程用来accept连接,要注意的是,虽然它是基于NIO的,但accept仍然采用阻塞模式。 这个线程不断阻塞调用server.accept()来接受连接并想SelectorManager里注册(新建立的SocketChannel则被配置为非阻塞模式)。


Acceptor线程的内部逻辑,循环调用accept来接受连接,并将连接Channel注册到SelectorManager里:
// start a thread to accept new connections
_manager.dispatch( new Runnable()
{
     public void run()
     {
         final ServerSocketChannel 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 = new Thread[getAcceptors()];
 
     for ( int i = 0 ; i < _acceptorThread.length; i++)
         _threadPool.dispatch( new Acceptor(i));
     if (_threadPool.isLowOnThreads())
         Log.warn( "insufficient threads configured for {}" , this );
}

Acceptor里的逻辑就是简单调用AbstractConnector中的抽象accept方法:
public void run()
{
     /* 省略... */
     try
     {
         current.setPriority(old_priority - _acceptorPriorityOffset);
         while (isRunning() && getConnection() != null)
         {
             try
             {
                 accept(_acceptor);
             }
             catch (...) { /* 省略... */ }
         }
     }
     finally { /* 省略... */ }
}

SelectChannelConnector里的accept,实际上做的根本就不是accept连接的动作,相反,它调用了SelectorManager的doSelect方法。
/* ------------------------------------------------------------ */
@Override
public void accept( int acceptorID) throws IOException
{
     _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 ;
     }
}

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值