启动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请求处理业务逻辑。_connection.handle()是真正的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(); } } } } }