mina框架源代碼研究

轉自:http://blog.csdn.net/gaolingep/archive/2009/05/04/4147697.aspx

第一部分:mina各層的關係

圖1.1

說明

  1. IOService:
    1. 這個接口抽象了一切I/O操作,包括建立端口監聽、I/O讀寫,
  2. IOProcessor:
    1. 爲IOService處理正真的I/O讀寫操作。 IOService在某端口監聽,當發生int selected = select();時,說明有建立連接的客戶端請求,IOService會調用IOProcessor,將session和交給processor處理。
    2. IOProcessor也會有一個int selected = select(1000);的阻塞,等待IO事件。
    3. 當I/O事件到達後,會把輸入讀入到一個BUF中session.getChannel().read(buf.buf())(Java NIO)
  3. IOFilter:
    1. 上面IOProcessor得到buf後,會交給 IOFilter處理,如果是讀取IO則觸發messageReceived 方法。
    2. IOFilter一般被包含在一個IoFilterChain中,顧名思義,當然是實現了責任鏈模式的一根鏈條咯。
  4. IoHandler
    1. 是拿到 buf 做處理的一個接口,是用戶最終會面對的一個接口。
    2. 上面的 IOFilter得到buf(以接收數據爲例),IOFilter調用了IoHandler messageReceived(s, message); 方法。

最後還要提一個接口:

IoSession

A handle which represents connection between two end - points regardless of

transport types.

實際上這個接口是一個會話服務,或者它更像是一個大容器。後面我們還會講到它。

 

第二部分:類圖

2.1 IOService

  1. IoAcceptor 接口:

服務器端接口,接受客戶端訪問的請求

  1. AbstractIoService

    設定處理函數、Filter、IoServiceListener

    1. 處理函數是一個Executor 或者是 Executor 的包裝。
    2. IoServiceListener 可以用於打印日誌,主要有 Service 啓動、停止、空閒等監聽方法
  2. AbstractIoAcceptor完成綁定監聽端口
  3. AbstractPollingIoAcceptor執行具體的監聽連接以及監聽I/O事件
  4. 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。實際上,這個接口也是用戶首先面對的接口。

  1. 用戶在這個端口上執行setHandler (IoHandler handler) 方法。
  2. 並執行 IoAcceptor 接口的 bind (SocketAddress localAddress) 方法。
  3. 實例化SocketAcceptor ,會 super() 其父類的構造函數。

我們依次看看三個父類的構造函數

  1. 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 就很好理解了,這是一個組成模式。

 

  1. AbstractIoAcceptor

protected AbstractIoAcceptor(IoSessionConfig sessionConfig, Executor executor) {

super (sessionConfig, executor);

defaultLocalAddresses .add( null );

}

AbstractIoAcceptor主要用來綁定監聽端口。這個構造函數沒有幹其他的事情。

 

  1. 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 乾的事情,去這個類中找。

 

  1. AbstractIoAcceptor bind 方法

// 端口打包成一個 list, 以實現多端口監聽

List localAddresses = new ArrayList(1);

localAddresses.add(localAddress);

 

然後調用了子類 AbstractPollingIoAcceptor bind0 方法

bind0(localAddressesCopy);

 

  1. 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 結束

 

  1. startupAcceptor(); 方法內部代碼。需要注意的是 Acceptor 不是 IOACCEPTOR ,這個 Acceptor 是一個線程,用於處理 IO 事件。

if ( acceptor == null ) {

     /*

                Acceptor 完成 監聽連接事件

                 如果有感興趣的連接發生,即調用 processHandles(selectedHandles());

     */

acceptor = new Acceptor();

// 調用爺爺類 AbstractIoService 的方法。

// acceptor 分配給線程池

executeWorker( acceptor );

}

 

  1. 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 循環

 

 

 

  1. 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);

}

}

}

 

  1. 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();

}

 

  1. 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();

}

 

  1. 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"

。。。。。。。。。。。。。。

 

 

  1. process(); 方法

private void process() throws Exception {

for (Iterator i = selectedSessions(); i.hasNext();) {

    T session = i.next();

process(session);

i.remove();

}

}

  1. process(); 方法

private void process(T session) {

if (isReadable(session) && !session.isReadSuspended()) {

read(session);

}

 

if (isWritable(session) && !session.isWriteSuspended()) {

scheduleFlush(session);

}

}

 

快結束了

  1. 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 ;

 

。。。。

}

 

  1. filetr 內的方法摘要

IoFilter filter = entry.getFilter();

//nextFilter 實例化了本類的 EntryImpl 內部類

NextFilter nextFilter = entry.getNextFilter();

 

// 這個 filter 會調用 IoFilterAdapter messageReceived

filter.messageReceived(nextFilter, session,

message);

             。。。

  1. 調用 handle 修成正果

session.getHandler().messageReceived(s, message);

 

3.4總結一下流程

  1. 3.4.1 初始化

實例化 SocketAcceptor main ->

完成 sessionConfig IoProcessor 的初始化【 SocketAcceptor 父類】 ->

acceptor.setHandler main

 

  1. 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

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