mina框架源碼閱讀與分析

Mina框架與源碼的簡單理解

 

.Mina架構圖:

其框架實現如下圖所示:(圖來自於網上:

 

.Mina 一個請求的主要實現流程:

       服務器啓動時,構造NioSocketAcceptor,服務器同時也會構造NioProcessor

    client請求->NioSocketAcceptor建立連接,在bind監聽端口後,調用startupAcceptor()方法->接收線程Acceptor啓動->processHandles()方法調用->初始化session,並把session加入到NioProcessor待處理session隊列中。

       當始化session,NioProcessor已經在通道中註冊可讀事件,同時add(T session)方法調用後->啓動startupProcessor()方法->處理線程Processor啓動->然後讀事件讀取數據時會在通道中執行過濾器連。

    如果有相應的業務處理,則調用相應的IoHandler(),然後組織返回給client的數據,經過數據經過濾連,數據發出。

 

一般我們自己寫nio服務器端時,只有一個selector,但mina框架使用了兩個選擇器selector,其中一個用來建立連接,其核心爲NioSocketAcceptor

另一個負責讀寫事件的,其核心爲NioProcessor

 

.NioSocketAcceptor相關說明

1.NioSocketAcceptor的父類爲AbstractPollingIoAcceptor,很多實現是在父類中實現的。在構造此類的時候,同時也構造了NioProcessor類,代碼如下:

protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig,

            Class<? extends IoProcessor<T>> processorClass) {

        this(sessionConfig, null, new SimpleIoProcessorPool<T>(processorClass),

                true);

    }

並且NioProcessor的個數爲cpu+1

private static final int DEFAULT_SIZE = Runtime.getRuntime()

            .availableProcessors() + 1;

2.init方法,打開selector通道;aceeptor()方法處理接收連接,把SocektChannel綁定到NioSession;open()方法是打開serversocketchannel,然後配製socket的一些基本屬性,並註冊此事件是可連接的事件。

socket.bind(localAddress, getBacklog());

// Register the channel within the selector for ACCEPT event

channel.register(selector, SelectionKey.OP_ACCEPT);

 

3.父類中有內部線程類Acceptor,主要負責輪詢處理註冊過連接事件的請求建立起連接。此線程類是在startupAcceptor()方法中調用,代碼如下:

private void startupAcceptor() {

        // If the acceptor is not ready, clear the queues

        // TODO : they should already be clean : do we have to do that ?

        if (!selectable) {

            registerQueue.clear();

            cancelQueue.clear();

        }

 

        // start the acceptor if not already started

        synchronized (lock) {

            if (acceptor == null) {

                acceptor = new Acceptor();

                executeWorker(acceptor);

            }

        }

}

 

.NioProcessor相關說明:

1.NioProcessor 在接收連接之後,另開了一個selector,用來處理讀寫事件。其大部分功能,在父類AbstractPollingIoProcessor中實現

 

2.Mina中用NioSession來封裝了channel對應的讀寫操作等功能.

 

3.NioProcessor類中init方法爲:

protected void init(NioSession session) throws Exception {

        SelectableChannel ch = (SelectableChannel) session.getChannel();

        ch.configureBlocking(false);

        session.setSelectionKey(ch.register(selector, SelectionKey.OP_READ, session));

}

很明顯註冊了讀事件,並與相應的NioSession對應起來。

 

4.NioProcessor類中表面上看代碼好像沒有註冊過寫事件,而且實際應用中,讀寫事件是來回切換的。但是實際上NioProcessor用另外的方法實現了:

setInterestedInRead()

setInterestedInWrite()

這兩個方法通過key.interestOps(newInterestOps)來實現事件切換,從而達到了註冊讀寫事件的作用。

要明白讀寫註冊事件,首先得明白SocketChannel註冊事件的原理,一個通道每次註冊時只對應一個事件,而每次註冊不同事件時,只是對此通道的事件進行了切換而已。所以通過key.interestOps(ops)方法是實現了最原始的操作,即切換通道事件。

 

5NioSessionreadwrite方法同Buffer中一樣。

 

6.IoSessionIterator<NioSession> an encapsulating iterator 。是NioProcess一個內部類

實現了iterator<NioSession> 接口的類,主要是重寫了其方法public NioSession next(),目的是把迭代key時,把NioSession取出。

public NioSession next() {

        SelectionKey key = iterator.next();

        NioSession nioSession =  (NioSession) key.attachment();

        return nioSession;

}

.NioProcessor 父類AbstractPollingIoProcessor的相關實現:

1.Processor,爲父類的內部線程類,在startProcessor()方法調用,通過executor啓動此線程。一般請求在NioSocketAcceptor上建立連接後,然後啓動NioProcessor中的工作線程Processor。一般也不是馬上企動的,需要在調用addNew方法後,有新的請求後纔會調用。

 

2.此類中有四個ConcurrentLinkedQueue分別用來存儲創建,刪除,操作中的session。其中值的說明的是flushingSessions是用來寫session

 

3.executorNioProcessor工作線程,默認實現爲:Executors.newCachedThreadPool()來定義。

 

4.startupProcessor()方法,開始工作線程,此處把processor內部線程類放入線程池中運行。如果select()出現阻塞,才馬上喚醒,在processor線程每次select時間爲一秒

private void startupProcessor() {

        synchronized (lock) {

            if (processor == null) {

                processor = new Processor();

                executor.execute(new NamePreservingRunnable(processor,

                        threadName));

            }

        }

 

        // Just stop the select() and start it again, so that the processor

        // can be activated immediately.

        wakeup();

  }

 

5.handNewSessions(T session) 從新session隊列中取出所有待處理的session.然後通過addNow(T Session)session init(),buildfilterchain過濾這些等。最後返回處理了session的數量。另外也有對應的removeSession()方法

 

6.read(T session)是讀取session裏面的數據。如果讀取數據的容易超過初始時設定的buffer大小時buffer會自動增加。寫數據是通過writeBuffer()方法實現,往外寫數據buffer大小也自動增加。

在此處要說明的,在此調用的readwritebuffer方法並不是最上層的方法,而是通過firechain連,調用fireMessageReceived(buf),以及fireMessageSent(session,buf)來讀寫。

請求到達時實例IoProcessor在會話通道中會執行過濾器鏈。

另外,如果此請求還有邏輯處理的話,接着也會執行相應的IoHandler

 

.MinaIoProcessor處理方式

       Mina 默認的線程工作方式爲一個IoProcessor 負責執行一個會話上的所有過濾器、IoHandler,也就是對於IO 讀寫操作來說,是單線程工作方式(就是按照順序逐個執行)。假如你想讓某個事件方法(譬如:sessionIdle()sessionOpened()等)在單獨的線程中運行(也就是非IoProcessor 所在的線程),那麼這裏就需要用到一個ExecutorFilter 的過濾器。

    增加一個ExecutorFilter代碼如下:

       acceptor.getFilterChain().addLast("exceutor", new ExecutorFilter()); 對於ExecutorFilter默認線程實現方式也可以修改。

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