轉自:http://blog.csdn.net/gaolingep/archive/2009/05/04/4147697.aspx
第一部分:mina各層的關係
圖1.1
說明
-
IOService:
-
這個接口抽象了一切I/O操作,包括建立端口監聽、I/O讀寫,
-
-
IOProcessor:
-
爲IOService處理正真的I/O讀寫操作。 IOService在某端口監聽,當發生int selected = select();時,說明有建立連接的客戶端請求,IOService會調用IOProcessor,將session和交給processor處理。
-
IOProcessor也會有一個int selected = select(1000);的阻塞,等待IO事件。
-
當I/O事件到達後,會把輸入讀入到一個BUF中session.getChannel().read(buf.buf())(Java NIO) 。
-
-
IOFilter:
-
上面IOProcessor得到buf後,會交給 IOFilter處理,如果是讀取IO則觸發messageReceived 方法。
-
IOFilter一般被包含在一個IoFilterChain中,顧名思義,當然是實現了責任鏈模式的一根鏈條咯。
-
-
IoHandler :
-
是拿到 buf 做處理的一個接口,是用戶最終會面對的一個接口。
-
上面的 IOFilter得到buf(以接收數據爲例),IOFilter調用了IoHandler 的 messageReceived(s, message); 方法。
-
最後還要提一個接口:
IoSession :
A handle which represents connection between two end - points regardless of
transport types.
實際上這個接口是一個會話服務,或者它更像是一個大容器。後面我們還會講到它。
第二部分:類圖
2.1 IOService
-
IoAcceptor 接口:
服務器端接口,接受客戶端訪問的請求
-
AbstractIoService
設定處理函數、Filter、IoServiceListener 。
-
處理函數是一個Executor 或者是 Executor 的包裝。
-
IoServiceListener 可以用於打印日誌,主要有 Service 啓動、停止、空閒等監聽方法
-
-
AbstractIoAcceptor完成綁定監聽端口
-
AbstractPollingIoAcceptor執行具體的監聽連接以及監聽I/O事件
-
NioSocketAcceptor用JAVA NIO的方式實現了具體的連接方法ServerSocketChannel ,例如 open , accept 等
2.2IoProcessor
顯然,這個結構實現了組成模式
SimpleIoProcessorPool內有一個數組,包裝了IoProcessor 。這個 SimpleIoProcessorPool的DEFAULT_SIZE 屬性, 定義了開多少個IoProcessor 。
前面講過 IoProcessor 爲IOService處理正真的I/O讀寫操作。(在nio下,一個IoProcessor 可以在單線程中處理多個連接請求和 io 事件)除此之外,再開多個 IoProcessor ,一個 IoProcessor 啓動一個線程。
2.3 IoSession
第三部分 源碼解讀
3.1初始化
SocketAcceptor acceptor = new NioSocketAcceptor();
在 圖1.1中,我們看到, mina 會首先把請求交給 IOService。實際上,這個接口也是用戶首先面對的接口。
-
用戶在這個端口上執行setHandler (IoHandler handler) 方法。
-
並執行 IoAcceptor 接口的 bind (SocketAddress localAddress) 方法。
-
實例化SocketAcceptor ,會 super() 其父類的構造函數。
我們依次看看三個父類的構造函數
-
AbstractPollingIoAcceptor
protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig,
Classextends IoProcessor> processorClass) {
this (sessionConfig, null , new SimpleIoProcessorPool(processorClass),
true );
}
-
構造函數AbstractPollingIoAcceptor(new DefaultSocketSessionConfig(), NioProcessor.class); 定義了默認的 SessionConfig 和 NioProcessor 。
-
new SimpleIoProcessorPool(processorClass) 是把 NioProcessor 包裝成了 pool. 看類圖 IoProcessor 就很好理解了,這是一個組成模式。
-
AbstractIoAcceptor
protected AbstractIoAcceptor(IoSessionConfig sessionConfig, Executor executor) {
super (sessionConfig, executor);
defaultLocalAddresses .add( null );
}
AbstractIoAcceptor主要用來綁定監聽端口。這個構造函數沒有幹其他的事情。
-
AbstractIoService
protected AbstractIoService(IoSessionConfig sessionConfig, Executor executor)
-
這個構造函數初始化 sessionConfig ,和當 executor 爲空時,
if (executor == null ) {
System. out .println( "[GL:executor:]" +executor);
this . executor = Executors.newCachedThreadPool ();
createdExecutor = true ;
} else {
this . executor = executor;
createdExecutor = false ;
}
-
這個構造函數還有一個監聽器,或者叫監聽池(可以包含多個監聽器)。用來監聽 service 創建、連接、斷開等動作,當上述動作發生地時候,會調用 listener 。裏面可以寫自己的一些方法。
3.2 初始化2
acceptor.setHandler( new EchoProtocolHandler());
設定處理函數, EchoProtocolHandler() 是用戶自己編寫的一個 handle ,看圖 1.1 可以知道,這是 mina 平臺處理的最後一步。 Mina 把請求經過 ioprocessor 處理器、多個 iofilter 過濾器 處理後,纔會交給 EchoProtocolHandler 。
我們看看從 IOService到IoHandler 之間發生了什麼
3.3運行
在main()函數中,我們看到一個方法
acceptor.bind( new InetSocketAddress( PORT ));
這一個 bind 方法,表面看只是一個端口綁定,實際上發生了很多有趣的事情。
IOService 類圖告訴我們, bind 端口應該是 AbstractIoAcceptor 乾的事情,去這個類中找。
-
AbstractIoAcceptor 類 bind 方法
// 端口打包成一個 list, 以實現多端口監聽
List localAddresses = new ArrayList(1);
localAddresses.add(localAddress);
然後調用了子類 AbstractPollingIoAcceptor 的 bind0 方法
bind0(localAddressesCopy);
- AbstractPollingIoAcceptor. bind0 方法,把代碼貼出來
protected final Set bind0(
Listextends SocketAddress> localAddresses) throws Exception {
// 這個 request 包裝了 localAddresses ,間接包裝了 localAddress
AcceptorOperationFuture request = new AcceptorOperationFuture(
localAddresses);
// adds the Registration request to the queue for the Workers
// to handle
registerQueue .add(request);
// creates the Acceptor instance and has the local
// executor kick it off.
/*
這個方法很重要,我們去看看發生了什麼
*/
startupAcceptor();
// wakeup() 即 selector.wakeup();
wakeup();
//bind0 結束
- startupAcceptor(); 方法內部代碼。需要注意的是 Acceptor 不是 IOACCEPTOR ,這個 Acceptor 是一個線程,用於處理 IO 事件。
if ( acceptor == null ) {
/*
Acceptor 完成 即監聽連接事件 ,
如果有感興趣的連接發生,即調用 processHandles(selectedHandles());
*/
acceptor = new Acceptor();
// 調用爺爺類 AbstractIoService 的方法。
// 把 acceptor 分配給線程池
executeWorker( acceptor );
}
- Acceptor ()代碼
private class Acceptor implements Runnable {
public void run() {
int nHandles = 0;
while ( selectable ) { // 這個循環用來監聽連接到達,以建立連接和 io 事件
try {
System. out .println( "[GaoLing:] 連接建立 " );
int selected = select(); // 如果一直沒有新連接,則一直會阻塞
/*
* 這個方法完成:打開 ServerSocketChannel 通道,註冊感興趣的監聽
將 ServerSocketChannel 丟到 boundHandles 中去
就是在定義的若干個端口放置監聽事件,接受 accept
注意:只是建立監聽而已!還沒有建立客戶端連接!!
for (SocketAddress a : localAddresses) {
/*
* 這個 open 函數會調用子類 NioSocketAcceptor 的 open 方法,
* 這個方法定義了一個通道,放置了一個感興趣的 key:SelectionKey.OP_ACCEPT
* 由 selector 去監聽
* 返回一個 ServerSocketChannel
*/
H handle = open(a);
open(a); 內部實現
ServerSocketChannel channel = ServerSocketChannel.open ();
channel.configureBlocking( false );
ServerSocket socket = channel.socket();
socket.bind(localAddress, getBacklog());
channel.register( selector , SelectionKey. OP_ACCEPT );
*/
nHandles += registerHandles();
// 如果有感興趣的事件到達
if (selected > 0) {
// We have some connection request, let's process
// them here.
// processHandles(selectedHandles()); 是很重要的方法,一會我們看看發生了什麼
// 開始調用 session ,
// 一旦有 IO in out 事件進來,調用這個函數。
// 這個函數有一個方法 session.getProcessor().add(session);
processHandles(selectedHandles());
}
。。。。。後面是回收處理,也很重要,但不在我們現在關注的範圍內
} // 大 while 循環
- processHandles 代碼
private void processHandles(Iterator handles) throws Exception {
// 注意這個 handles: 是 ServerSocketChannelIterator, 不是我們的 IoHandler !
while (handles.hasNext()) {
H handle = handles.next();
handles.remove();
/*
注意, session 出來了
* 參數 1 : processor 就是 SimpleIoProcessorPool 的實例 , 第一部分講了來歷,這裏再提一下
* 在 AbstractPollingIoAcceptor 有構造函數
* protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig,
Class> processorClass) {
this(sessionConfig, null, new SimpleIoProcessorPool(processorClass) ,
true);
}
對 NioProcessor 進行了包裝,成爲 SimpleIoProcessorPool 。
* 參數 2 : handle 包裝了 selector.selectedKeys()
* 調用子類 NioSocketAcceptor 的 accept 方法,
* 這個方法:
* SocketChannel client=server.accept();// 查找客戶端套接字通道
* 初始化並返回 NioSocketSession 對象 return new NioSocketSession(this, processor, ch);
*/
T session = accept( processor , handle);
if (session == null ) {
break ;
}
// 這個方法比較複雜,不是我們今天講得重點,先跳過去
//setAttributeMap
//setWriteRequestQueue
finishSessionInitialization(session, null , null );
// add the session to the SocketIoProcessor
// 完成了 session 和 process 的綁定,並開始處理 handle
//session.getProcessor() 返回 SimpleIoProcessorPool
// 這個方法開啓了一個線程,調用了 AbstractPollingIoProcessor 的 add 方法
// 實際上上面我們說了, session.getProcessor() 得到的是 包裝了 NioProcessor 對象的 SimpleIoProcessorPool , NioProcessor 對象間接繼承了 add 方法
// 看看這個 add 方法,調用了 startupWorker(); ,開啓了一個線程
session.getProcessor().add(session);
}
}
}
-
session.getProcessor().add(session); 方法
這個方法調用了 SimpleIoProcessorPool.add 。即引出了自定義線程池。參看類圖 Processor。具體就跳過去了。
public final void add(T session) {
if (isDisposing()) {
throw new IllegalStateException( "Already disposed." );
}
// Adds the session to the newSession queue and starts the worker
newSessions .add(session);
startupWorker();
}
- startupWorker() 方法
private void startupWorker() {
synchronized ( lock ) {
// 這個 processor 不是 IOProcessor, 它只是一個線程,每隔 1s 種逐一通知 session
// 這個線程會一直輪詢下去, for(;;)
if ( processor == null ) {
processor = new Processor();
// 注: session 已被加載到 newSessions.add(session); 中
executor .execute( new NamePreservingRunnable( processor , threadName ));
}
}
wakeup();
}
- Processor 方法
private class Processor implements Runnable {
public void run() {
。。。。。。。。
for (;;) {
try {
// 調用子類 NioProcessor 的 return selector.select(timeout);
int selected = select(1000);
if (selected > 0) { // 如果有連接過來,就調用 process 方法
process();
}
// 否則執行 "idle"
。。。。。。。。。。。。。。
- process(); 方法
private void process() throws Exception {
for (Iterator i = selectedSessions(); i.hasNext();) {
T session = i.next();
process(session);
i.remove();
}
}
- process(); 方法
private void process(T session) {
if (isReadable(session) && !session.isReadSuspended()) {
read(session);
}
if (isWritable(session) && !session.isWriteSuspended()) {
scheduleFlush(session);
}
}
快結束了
- read 方法
private void read(T session) {
IoSessionConfig config = session.getConfig();
//buf 存放了 channel.read 的結果
IoBuffer buf = IoBuffer.allocate (config.getReadBufferSize());
final boolean hasFragmentation =
session.getTransportMetadata().hasFragmentation();
try {
int readBytes = 0;
int ret;
// 初始化 buffer
。。。。。。
if (readBytes > 0) {
/ / session.getFilterChain(), 以前沒有講過 filterChain 是從哪裏來的 ,但是提到過 session 是 NioSocketSession 的實例,這個類中定義了
//private final IoFilterChain filterChain = new DefaultIoFilterChain(this);
IoFilterChain filterChain = session.getFilterChain();
// 這句話包含了調用 handle ,把 buf 內容推送給它
//filterChain 是 DefaultIoFilterChain 的實例
// 在這個方法裏面,會調用用戶 handle 的方法!
filterChain.fireMessageReceived(buf);
buf = null ;
。。。。
}
- filetr 內的方法摘要
IoFilter filter = entry.getFilter();
//nextFilter 實例化了本類的 EntryImpl 的 內部類
NextFilter nextFilter = entry.getNextFilter();
// 這個 filter 會調用 IoFilterAdapter 的 messageReceived
filter.messageReceived(nextFilter, session,
message);
。。。
- 調用 handle 修成正果
session.getHandler().messageReceived(s, message);
3.4總結一下流程
- 3.4.1 初始化
實例化 SocketAcceptor 【 main 】 ->
完成 sessionConfig 、 IoProcessor 的初始化【 SocketAcceptor 父類】 ->
acceptor.setHandler 【 main 】
- 3.4.2 運行(對照類圖看)
acceptor.bind( new InetSocketAddress( PORT )); 【 main 】 ->
AbstractIoAcceptor.bind 【 AbstractIoAcceptor 】 ->
AbstractPollingIoAcceptor. bind0 【 AbstractPollingIoAcceptor 】 ->
startupAcceptor() 【開線程,監聽連接事件, AbstractPollingIoAcceptor 】 ->
class Acceptor 【 AbstractPollingIoAcceptor 】 ->
processHandles(Iterator handles) 【 AbstractPollingIoAcceptor 】 ->
T session = accept( processor , handle); 【初始化 session, AbstractPollingIoAcceptor 】 ->
session.getProcessor().add(session); 【處理 io 事件 ,AbstractPollingIoAcceptor 】 ->
SimpleIoProcessorPool.add 【這裏用到了自定義線程池(數組), SimpleIoProcessorPool 】 ->
AbstractPollingIoProcessor.add 【調用 startupWorker() , AbstractPollingIoProcessor 】 ->
startupWorker(); 【處理 io 事件。 AbstractPollingIoProcessor 】 ->
processor = new Processor(); 【線程池,處理 IO 輸入輸出數據, AbstractPollingIoProcessor 】 ->
process() 【 AbstractPollingIoProcessor 】 ->
read(T session) 【 AbstractPollingIoProcessor 】 ->
filterChain->
handle