概述
jetty NIO是典型reactor模型,如下圖所示:
即:mainReactor負責監聽server socket,接受新連接,並將建立的socket分派給subReactor。subReactor負責多路分離已連接的socket,讀寫網絡數據,扔給worker線程池來處理。本文主要是講解jetty中mainReactor、subReactor、線程池的實現。
mainReactor
jetty中的server就相當於一個容器,一個jetty容器包含多個連接器和一個線程池,連接器實現了LifeCycle接口,隨容器啓動而啓動,下圖是連接器啓動後,監聽server socket,建立連接的過程:
可見,jetty利用了線程池來建立連接,每一個連接任務被當成一個job被放到了job隊列裏面,負責連接的線程會從隊列中取出任務來執行,將得到的ServerSocket交給subReactor,下面來看subReactor的實現。
subReactor
這裏需要提一下jetty nio很重要的一個類SelectorManager,它負責channel註冊,select,wakeup等操作。在SelectorManager中有SelectSet數組,可以把SelectSet理解爲SelectorManager的代理,因爲真正做事的是SelectSet,這裏面SelectSet設計爲一個數組,應該也是分而治之的思想,讓一個selector監聽更少的selectionkey。
SelectSet中有一個非常重要的成員changes,changes中存放了所有有變化的channel、endpoint、attachement。分別在以下情況觸發addChannel方法:當有新的通道加入時,當有新的事件到來時,當有數據到來時。
subReactor的執行流程如下圖:
在這裏導致addChange除了selectorManager.register之外,還有endpoint.updatekey()以及selectionkey數據有變化時等等。
ThreadPool
jetty的線程池相當簡單,其實mainReactor與subReactor共用同一個線程池,線程池的實現類是QueuedThreadPool,當然在jetty.xml中可以設置自己的線程池類。簡單看下線程池的run方法
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)
{
...
}
}
};
1、線程池有個最小線程數_minThreads=8,當線程池啓動時會創建_minThreads個線程,並啓動它們。第12行,線程從任務隊列中取出一個任務,並執行。這裏使用了while循環表示這裏會阻塞等待任務執行完,當任務隊列中沒有任務時,纔會退出while循環;2、退出while循環後,這個線程就空閒了,在這裏需要有個回收策略,在等待_maxIdleTimeMs時間後,如果當前線程數大於_minThreads時,就會回收這個線程。
那麼線程數什麼時候會大於_minThreads?來看看dispatch()方法中的核心代碼
// 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);
}
如果沒有空閒的線程或者空閒線程數太少,在保證線程數沒有超過_maxThreads時會新建線程。